pulseengine_mcp_auth/security/
request_security.rs

1//! MCP Request Security Validation and Sanitization
2//!
3//! This module provides comprehensive security validation for MCP requests,
4//! including parameter sanitization, size limits, and injection protection.
5
6use crate::AuthContext;
7use pulseengine_mcp_protocol::Request;
8use regex::Regex;
9use serde_json::Value;
10use std::collections::{HashMap, HashSet};
11use thiserror::Error;
12use tracing::{debug, error, warn};
13
14/// Errors that can occur during security validation
15#[derive(Debug, Error)]
16pub enum SecurityValidationError {
17    #[error("Request too large: {current} bytes exceeds limit of {limit} bytes")]
18    RequestTooLarge { current: usize, limit: usize },
19
20    #[error("Parameter value too large: {param} has {current} bytes, limit is {limit} bytes")]
21    ParameterTooLarge {
22        param: String,
23        current: usize,
24        limit: usize,
25    },
26
27    #[error("Too many parameters: {current} exceeds limit of {limit}")]
28    TooManyParameters { current: usize, limit: usize },
29
30    #[error("Invalid parameter name: {name}")]
31    InvalidParameterName { name: String },
32
33    #[error("Potential injection attack detected in parameter: {param}")]
34    InjectionDetected { param: String },
35
36    #[error("Malicious content detected: {reason}")]
37    MaliciousContent { reason: String },
38
39    #[error("Rate limit exceeded for method: {method}")]
40    RateLimitExceeded { method: String },
41
42    #[error("Unsupported method: {method}")]
43    UnsupportedMethod { method: String },
44}
45
46/// Security violation details for logging and monitoring
47#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
48pub struct SecurityViolation {
49    /// Type of violation
50    pub violation_type: SecurityViolationType,
51
52    /// Severity level
53    pub severity: SecuritySeverity,
54
55    /// Description of the violation
56    pub description: String,
57
58    /// Parameter or field involved
59    pub field: Option<String>,
60
61    /// Original value that triggered the violation
62    pub value: Option<String>,
63
64    /// Timestamp of the violation
65    pub timestamp: chrono::DateTime<chrono::Utc>,
66}
67
68/// Types of security violations
69#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
70pub enum SecurityViolationType {
71    SizeLimit,
72    ParameterLimit,
73    InjectionAttempt,
74    MaliciousContent,
75    InvalidFormat,
76    RateLimit,
77    UnauthorizedMethod,
78}
79
80/// Security severity levels
81#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
82pub enum SecuritySeverity {
83    Low,
84    Medium,
85    High,
86    Critical,
87}
88
89/// Configuration for request size and complexity limits
90#[derive(Debug, Clone)]
91pub struct RequestLimitsConfig {
92    /// Maximum request size in bytes
93    pub max_request_size: usize,
94
95    /// Maximum number of parameters
96    pub max_parameters: usize,
97
98    /// Maximum size for any single parameter value
99    pub max_parameter_size: usize,
100
101    /// Maximum string length for text parameters
102    pub max_string_length: usize,
103
104    /// Maximum array length
105    pub max_array_length: usize,
106
107    /// Maximum object depth (nested objects)
108    pub max_object_depth: usize,
109
110    /// Maximum number of keys in an object
111    pub max_object_keys: usize,
112}
113
114impl Default for RequestLimitsConfig {
115    fn default() -> Self {
116        Self {
117            max_request_size: 10 * 1024 * 1024, // 10MB
118            max_parameters: 100,
119            max_parameter_size: 1024 * 1024, // 1MB
120            max_string_length: 10000,
121            max_array_length: 1000,
122            max_object_depth: 10,
123            max_object_keys: 100,
124        }
125    }
126}
127
128/// Configuration for request security validation
129#[derive(Debug, Clone)]
130pub struct RequestSecurityConfig {
131    /// Enable request validation
132    pub enabled: bool,
133
134    /// Request size and complexity limits
135    pub limits: RequestLimitsConfig,
136
137    /// Enable injection attack detection
138    pub enable_injection_detection: bool,
139
140    /// Enable parameter sanitization
141    pub enable_sanitization: bool,
142
143    /// Allowed methods (empty means all allowed)
144    pub allowed_methods: HashSet<String>,
145
146    /// Blocked methods
147    pub blocked_methods: HashSet<String>,
148
149    /// Enable rate limiting per method
150    pub enable_method_rate_limiting: bool,
151
152    /// Method rate limits (method -> requests per minute)
153    pub method_rate_limits: HashMap<String, u32>,
154
155    /// Log security violations
156    pub log_violations: bool,
157
158    /// Fail on security violations (vs warn and continue)
159    pub fail_on_violations: bool,
160}
161
162impl Default for RequestSecurityConfig {
163    fn default() -> Self {
164        let mut method_rate_limits = HashMap::new();
165        method_rate_limits.insert("tools/call".to_string(), 60); // 1 per second
166        method_rate_limits.insert("resources/read".to_string(), 120); // 2 per second
167
168        Self {
169            enabled: true,
170            limits: RequestLimitsConfig::default(),
171            enable_injection_detection: true,
172            enable_sanitization: true,
173            allowed_methods: HashSet::new(), // Empty means all allowed
174            blocked_methods: HashSet::new(),
175            enable_method_rate_limiting: false, // Disabled by default
176            method_rate_limits,
177            log_violations: true,
178            fail_on_violations: true,
179        }
180    }
181}
182
183/// Input sanitizer for removing/escaping dangerous content
184pub struct InputSanitizer {
185    /// SQL injection patterns
186    sql_patterns: Vec<Regex>,
187
188    /// XSS patterns
189    xss_patterns: Vec<Regex>,
190
191    /// Command injection patterns
192    command_patterns: Vec<Regex>,
193
194    /// Path traversal patterns
195    path_traversal_patterns: Vec<Regex>,
196}
197
198impl InputSanitizer {
199    /// Create a new input sanitizer
200    pub fn new() -> Self {
201        Self {
202            sql_patterns: Self::build_sql_patterns(),
203            xss_patterns: Self::build_xss_patterns(),
204            command_patterns: Self::build_command_patterns(),
205            path_traversal_patterns: Self::build_path_traversal_patterns(),
206        }
207    }
208
209    /// Build SQL injection detection patterns
210    fn build_sql_patterns() -> Vec<Regex> {
211        let patterns = [
212            r"(?i)(union\s+select|select\s+.*\s+from|insert\s+into|delete\s+from|drop\s+table)",
213            r"(?i)(exec\s*\(|execute\s*\(|sp_|xp_)",
214            r"(?i)(\bor\b\s+\d+\s*=\s*\d+|\band\b\s+\d+\s*=\s*\d+)",
215            r"(?i)(sleep\s*\(|benchmark\s*\(|waitfor\s+delay)",
216            r#"['";]\s*(\bunion\b|\bselect\b|\binsert\b|\bdelete\b|\bdrop\b)"#,
217        ];
218
219        patterns
220            .iter()
221            .filter_map(|pattern| Regex::new(pattern).ok())
222            .collect()
223    }
224
225    /// Build XSS detection patterns
226    fn build_xss_patterns() -> Vec<Regex> {
227        let patterns = [
228            r"(?i)<script[^>]*>.*?</script>",
229            r"(?i)javascript:",
230            r"(?i)on\w+\s*=",
231            r"(?i)<iframe[^>]*>.*?</iframe>",
232            r"(?i)eval\s*\(",
233        ];
234
235        patterns
236            .iter()
237            .filter_map(|pattern| Regex::new(pattern).ok())
238            .collect()
239    }
240
241    /// Build command injection detection patterns
242    fn build_command_patterns() -> Vec<Regex> {
243        let patterns = [
244            r"[;&|`$()]",
245            r"(?i)(cmd|powershell|bash|sh)\s",
246            r"\.\.\/",
247            r"(?i)(\bcat\b|\bls\b|\bpwd\b|\bwhoami\b|\bps\b|\btop\b)",
248        ];
249
250        patterns
251            .iter()
252            .filter_map(|pattern| Regex::new(pattern).ok())
253            .collect()
254    }
255
256    /// Build path traversal detection patterns
257    fn build_path_traversal_patterns() -> Vec<Regex> {
258        let patterns = [
259            r"\.\.\/",
260            r"\.\.\\",
261            r"%2e%2e%2f",
262            r"%2e%2e%5c",
263            r"(?i)\.\.[\\/]",
264        ];
265
266        patterns
267            .iter()
268            .filter_map(|pattern| Regex::new(pattern).ok())
269            .collect()
270    }
271
272    /// Check if a string contains potential injection attempts
273    pub fn detect_injection(&self, value: &str) -> Vec<String> {
274        let mut violations = Vec::new();
275
276        // Check SQL injection
277        for pattern in &self.sql_patterns {
278            if pattern.is_match(value) {
279                violations.push("SQL injection attempt detected".to_string());
280                break;
281            }
282        }
283
284        // Check XSS
285        for pattern in &self.xss_patterns {
286            if pattern.is_match(value) {
287                violations.push("XSS attempt detected".to_string());
288                break;
289            }
290        }
291
292        // Check command injection
293        for pattern in &self.command_patterns {
294            if pattern.is_match(value) {
295                violations.push("Command injection attempt detected".to_string());
296                break;
297            }
298        }
299
300        // Check path traversal
301        for pattern in &self.path_traversal_patterns {
302            if pattern.is_match(value) {
303                violations.push("Path traversal attempt detected".to_string());
304                break;
305            }
306        }
307
308        violations
309    }
310
311    /// Sanitize a string by removing/escaping dangerous content
312    pub fn sanitize_string(&self, value: &str) -> String {
313        let mut sanitized = value.to_string();
314
315        // Remove null bytes
316        sanitized = sanitized.replace('\0', "");
317
318        // Escape potentially dangerous characters
319        sanitized = sanitized.replace('<', "&lt;");
320        sanitized = sanitized.replace('>', "&gt;");
321        sanitized = sanitized.replace('\"', "&quot;");
322        sanitized = sanitized.replace('\'', "&#x27;");
323
324        // Remove control characters (except \t, \n, \r)
325        sanitized = sanitized
326            .chars()
327            .filter(|&c| !c.is_control() || c == '\t' || c == '\n' || c == '\r')
328            .collect();
329
330        sanitized
331    }
332}
333
334impl Default for InputSanitizer {
335    fn default() -> Self {
336        Self::new()
337    }
338}
339
340/// Main request security validator
341pub struct RequestSecurityValidator {
342    config: RequestSecurityConfig,
343    sanitizer: InputSanitizer,
344    violation_log: std::sync::Arc<std::sync::Mutex<Vec<SecurityViolation>>>,
345}
346
347impl RequestSecurityValidator {
348    /// Create a new request security validator
349    pub fn new(config: RequestSecurityConfig) -> Self {
350        Self {
351            config,
352            sanitizer: InputSanitizer::new(),
353            violation_log: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
354        }
355    }
356
357    /// Create with default configuration
358    pub fn default() -> Self {
359        Self::new(RequestSecurityConfig::default())
360    }
361
362    /// Validate an MCP request for security issues
363    pub async fn validate_request(
364        &self,
365        request: &Request,
366        auth_context: Option<&AuthContext>,
367    ) -> Result<(), SecurityValidationError> {
368        if !self.config.enabled {
369            return Ok(());
370        }
371
372        debug!("Validating request security for method: {}", request.method);
373
374        // Apply user-specific security rules based on authentication context
375        if let Some(context) = auth_context {
376            self.validate_user_specific_rules(request, context)?;
377        }
378
379        // Validate method
380        self.validate_method(&request.method)?;
381
382        // Validate request size
383        let request_size = serde_json::to_string(request)
384            .map_err(|_| SecurityValidationError::MaliciousContent {
385                reason: "Request serialization failed".to_string(),
386            })?
387            .len();
388
389        if request_size > self.config.limits.max_request_size {
390            self.log_violation(SecurityViolation {
391                violation_type: SecurityViolationType::SizeLimit,
392                severity: SecuritySeverity::High,
393                description: format!(
394                    "Request size {} exceeds limit {}",
395                    request_size, self.config.limits.max_request_size
396                ),
397                field: None,
398                value: None,
399                timestamp: chrono::Utc::now(),
400            });
401
402            return Err(SecurityValidationError::RequestTooLarge {
403                current: request_size,
404                limit: self.config.limits.max_request_size,
405            });
406        }
407
408        // Validate parameters
409        self.validate_parameters(&request.params, "params")?;
410
411        // Check for injection attempts
412        if self.config.enable_injection_detection {
413            self.detect_injection_attempts(&request.params, "params")?;
414        }
415
416        debug!("Request passed security validation");
417        Ok(())
418    }
419
420    /// Sanitize an MCP request
421    pub async fn sanitize_request(&self, mut request: Request) -> Request {
422        if !self.config.enabled || !self.config.enable_sanitization {
423            return request;
424        }
425
426        debug!("Sanitizing request parameters");
427        request.params = self.sanitize_value(&request.params);
428        request
429    }
430
431    /// Validate method name
432    fn validate_method(&self, method: &str) -> Result<(), SecurityValidationError> {
433        // Check blocked methods
434        if self.config.blocked_methods.contains(method) {
435            return Err(SecurityValidationError::UnsupportedMethod {
436                method: method.to_string(),
437            });
438        }
439
440        // Check allowed methods (if specified)
441        if !self.config.allowed_methods.is_empty() && !self.config.allowed_methods.contains(method)
442        {
443            return Err(SecurityValidationError::UnsupportedMethod {
444                method: method.to_string(),
445            });
446        }
447
448        Ok(())
449    }
450
451    /// Validate parameters recursively
452    fn validate_parameters(
453        &self,
454        value: &Value,
455        path: &str,
456    ) -> Result<(), SecurityValidationError> {
457        self.validate_value_size(value, path)?;
458
459        match value {
460            Value::Object(obj) => {
461                if obj.len() > self.config.limits.max_object_keys {
462                    return Err(SecurityValidationError::TooManyParameters {
463                        current: obj.len(),
464                        limit: self.config.limits.max_object_keys,
465                    });
466                }
467
468                for (key, val) in obj {
469                    let new_path = format!("{}.{}", path, key);
470                    self.validate_parameters(val, &new_path)?;
471                }
472            }
473            Value::Array(arr) => {
474                if arr.len() > self.config.limits.max_array_length {
475                    return Err(SecurityValidationError::TooManyParameters {
476                        current: arr.len(),
477                        limit: self.config.limits.max_array_length,
478                    });
479                }
480
481                for (i, val) in arr.iter().enumerate() {
482                    let new_path = format!("{}[{}]", path, i);
483                    self.validate_parameters(val, &new_path)?;
484                }
485            }
486            Value::String(s) => {
487                if s.len() > self.config.limits.max_string_length {
488                    return Err(SecurityValidationError::ParameterTooLarge {
489                        param: path.to_string(),
490                        current: s.len(),
491                        limit: self.config.limits.max_string_length,
492                    });
493                }
494            }
495            _ => {} // Other types are fine
496        }
497
498        Ok(())
499    }
500
501    /// Validate the size of a value
502    fn validate_value_size(
503        &self,
504        value: &Value,
505        path: &str,
506    ) -> Result<(), SecurityValidationError> {
507        let size = serde_json::to_string(value)
508            .map_err(|_| SecurityValidationError::MaliciousContent {
509                reason: "Parameter serialization failed".to_string(),
510            })?
511            .len();
512
513        if size > self.config.limits.max_parameter_size {
514            return Err(SecurityValidationError::ParameterTooLarge {
515                param: path.to_string(),
516                current: size,
517                limit: self.config.limits.max_parameter_size,
518            });
519        }
520
521        Ok(())
522    }
523
524    /// Detect injection attempts in parameters
525    fn detect_injection_attempts(
526        &self,
527        value: &Value,
528        path: &str,
529    ) -> Result<(), SecurityValidationError> {
530        match value {
531            Value::String(s) => {
532                let violations = self.sanitizer.detect_injection(s);
533                if !violations.is_empty() {
534                    self.log_violation(SecurityViolation {
535                        violation_type: SecurityViolationType::InjectionAttempt,
536                        severity: SecuritySeverity::Critical,
537                        description: violations.join(", "),
538                        field: Some(path.to_string()),
539                        value: Some(s.clone()),
540                        timestamp: chrono::Utc::now(),
541                    });
542
543                    return Err(SecurityValidationError::InjectionDetected {
544                        param: path.to_string(),
545                    });
546                }
547            }
548            Value::Object(obj) => {
549                for (key, val) in obj {
550                    let new_path = format!("{}.{}", path, key);
551                    self.detect_injection_attempts(val, &new_path)?;
552                }
553            }
554            Value::Array(arr) => {
555                for (i, val) in arr.iter().enumerate() {
556                    let new_path = format!("{}[{}]", path, i);
557                    self.detect_injection_attempts(val, &new_path)?;
558                }
559            }
560            _ => {} // Other types are safe
561        }
562
563        Ok(())
564    }
565
566    /// Sanitize a JSON value recursively
567    fn sanitize_value(&self, value: &Value) -> Value {
568        match value {
569            Value::String(s) => Value::String(self.sanitizer.sanitize_string(s)),
570            Value::Object(obj) => {
571                let sanitized_obj: serde_json::Map<String, Value> = obj
572                    .iter()
573                    .map(|(k, v)| (k.clone(), self.sanitize_value(v)))
574                    .collect();
575                Value::Object(sanitized_obj)
576            }
577            Value::Array(arr) => {
578                let sanitized_arr: Vec<Value> =
579                    arr.iter().map(|v| self.sanitize_value(v)).collect();
580                Value::Array(sanitized_arr)
581            }
582            _ => value.clone(), // Numbers, bools, null are safe
583        }
584    }
585
586    /// Log a security violation
587    fn log_violation(&self, violation: SecurityViolation) {
588        if self.config.log_violations {
589            match violation.severity {
590                SecuritySeverity::Critical => {
591                    error!("Critical security violation: {}", violation.description)
592                }
593                SecuritySeverity::High => {
594                    warn!("High security violation: {}", violation.description)
595                }
596                SecuritySeverity::Medium => {
597                    warn!("Medium security violation: {}", violation.description)
598                }
599                SecuritySeverity::Low => {
600                    debug!("Low security violation: {}", violation.description)
601                }
602            }
603        }
604
605        if let Ok(mut log) = self.violation_log.lock() {
606            log.push(violation);
607
608            // Keep only last 1000 violations to prevent memory bloat
609            if log.len() > 1000 {
610                log.drain(0..100);
611            }
612        }
613    }
614
615    /// Get recent security violations
616    pub fn get_violations(&self) -> Vec<SecurityViolation> {
617        self.violation_log
618            .lock()
619            .map(|log| log.clone())
620            .unwrap_or_default()
621    }
622
623    /// Clear violation log
624    pub fn clear_violations(&self) {
625        if let Ok(mut log) = self.violation_log.lock() {
626            log.clear();
627        }
628    }
629
630    /// Validate user-specific security rules based on authentication context
631    fn validate_user_specific_rules(
632        &self,
633        request: &Request,
634        auth_context: &AuthContext,
635    ) -> Result<(), SecurityValidationError> {
636        // Apply stricter limits for lower-privilege users
637        let user_limits = self.get_user_specific_limits(auth_context);
638
639        // Validate request size against user-specific limits
640        let request_size = serde_json::to_string(request)
641            .map_err(|_| SecurityValidationError::MaliciousContent {
642                reason: "Request serialization failed for user validation".to_string(),
643            })?
644            .len();
645
646        if request_size > user_limits.max_request_size {
647            self.log_violation(SecurityViolation {
648                violation_type: SecurityViolationType::SizeLimit,
649                severity: SecuritySeverity::High,
650                description: format!(
651                    "User {} exceeded request size limit: {} > {}",
652                    auth_context.user_id.as_deref().unwrap_or("unknown"),
653                    request_size,
654                    user_limits.max_request_size
655                ),
656                field: None,
657                value: None,
658                timestamp: chrono::Utc::now(),
659            });
660
661            return Err(SecurityValidationError::RequestTooLarge {
662                current: request_size,
663                limit: user_limits.max_request_size,
664            });
665        }
666
667        // Apply method-specific restrictions based on user role
668        if let Some(restricted_methods) = self.get_restricted_methods_for_user(auth_context) {
669            if restricted_methods.contains(&request.method) {
670                self.log_violation(SecurityViolation {
671                    violation_type: SecurityViolationType::UnauthorizedMethod,
672                    severity: SecuritySeverity::Critical,
673                    description: format!(
674                        "User {} attempted to access restricted method: {}",
675                        auth_context.user_id.as_deref().unwrap_or("unknown"),
676                        request.method
677                    ),
678                    field: Some("method".to_string()),
679                    value: Some(request.method.clone()),
680                    timestamp: chrono::Utc::now(),
681                });
682
683                return Err(SecurityValidationError::UnsupportedMethod {
684                    method: request.method.clone(),
685                });
686            }
687        }
688
689        // Apply enhanced injection detection for anonymous users
690        if auth_context.user_id.is_none() {
691            // Anonymous users get stricter validation
692            self.validate_anonymous_user_request(request)?;
693        }
694
695        Ok(())
696    }
697
698    /// Get user-specific request limits based on role and permissions
699    fn get_user_specific_limits(&self, auth_context: &AuthContext) -> RequestLimitsConfig {
700        use crate::models::Role;
701
702        // Default to the configured limits
703        let mut limits = self.config.limits.clone();
704
705        // Apply role-based limits
706        let has_admin_role = auth_context
707            .roles
708            .iter()
709            .any(|role| matches!(role, Role::Admin));
710        let has_operator_role = auth_context
711            .roles
712            .iter()
713            .any(|role| matches!(role, Role::Operator));
714        let has_device_role = auth_context
715            .roles
716            .iter()
717            .any(|role| matches!(role, Role::Device { .. }));
718
719        if has_device_role && !has_admin_role {
720            // Devices get smaller limits to prevent resource exhaustion
721            limits.max_request_size = std::cmp::min(limits.max_request_size, 64 * 1024); // 64KB max
722            limits.max_parameter_size = std::cmp::min(limits.max_parameter_size, 8 * 1024); // 8KB max
723            limits.max_string_length = std::cmp::min(limits.max_string_length, 1000);
724            limits.max_array_length = std::cmp::min(limits.max_array_length, 50);
725            limits.max_object_keys = std::cmp::min(limits.max_object_keys, 20);
726        } else if !has_admin_role && !has_operator_role {
727            // Regular users get moderate limits
728            limits.max_request_size = std::cmp::min(limits.max_request_size, 256 * 1024); // 256KB max
729            limits.max_parameter_size = std::cmp::min(limits.max_parameter_size, 32 * 1024); // 32KB max
730            limits.max_string_length = std::cmp::min(limits.max_string_length, 5000);
731            limits.max_array_length = std::cmp::min(limits.max_array_length, 200);
732            limits.max_object_keys = std::cmp::min(limits.max_object_keys, 50);
733        }
734        // Admins and operators get full configured limits
735
736        limits
737    }
738
739    /// Get restricted methods for specific user based on role and permissions
740    fn get_restricted_methods_for_user(
741        &self,
742        auth_context: &AuthContext,
743    ) -> Option<HashSet<String>> {
744        use crate::models::Role;
745
746        let has_admin_role = auth_context
747            .roles
748            .iter()
749            .any(|role| matches!(role, Role::Admin));
750
751        // Admins have no method restrictions
752        if has_admin_role {
753            return None;
754        }
755
756        let mut restricted = HashSet::new();
757
758        // Device role restrictions
759        let has_device_role = auth_context
760            .roles
761            .iter()
762            .any(|role| matches!(role, Role::Device { .. }));
763        if has_device_role {
764            // Devices cannot access administrative methods
765            restricted.insert("logging/setLevel".to_string());
766            restricted.insert("server/shutdown".to_string());
767            restricted.insert("auth/createKey".to_string());
768            restricted.insert("auth/revokeKey".to_string());
769        }
770
771        // Monitor role restrictions
772        let has_monitor_role = auth_context
773            .roles
774            .iter()
775            .any(|role| matches!(role, Role::Monitor));
776        if has_monitor_role
777            && !auth_context
778                .roles
779                .iter()
780                .any(|role| matches!(role, Role::Operator))
781        {
782            // Monitor-only users cannot access state-changing methods
783            restricted.insert("tools/call".to_string());
784            restricted.insert("resources/write".to_string());
785        }
786
787        if restricted.is_empty() {
788            None
789        } else {
790            Some(restricted)
791        }
792    }
793
794    /// Apply enhanced validation for anonymous users
795    fn validate_anonymous_user_request(
796        &self,
797        request: &Request,
798    ) -> Result<(), SecurityValidationError> {
799        // Check method parameters more strictly
800        self.detect_injection_attempts_strict(&request.params, "params")?;
801
802        // Anonymous users are limited to read-only operations
803        let read_only_methods = [
804            "ping",
805            "initialize",
806            "resources/list",
807            "resources/read",
808            "tools/list",
809            "completion/complete",
810        ];
811
812        if !read_only_methods.contains(&request.method.as_str()) {
813            self.log_violation(SecurityViolation {
814                violation_type: SecurityViolationType::UnauthorizedMethod,
815                severity: SecuritySeverity::High,
816                description: format!(
817                    "Anonymous user attempted non-read-only method: {}",
818                    request.method
819                ),
820                field: Some("method".to_string()),
821                value: Some(request.method.clone()),
822                timestamp: chrono::Utc::now(),
823            });
824
825            return Err(SecurityValidationError::UnsupportedMethod {
826                method: request.method.clone(),
827            });
828        }
829
830        Ok(())
831    }
832
833    /// Enhanced injection detection with stricter rules
834    fn detect_injection_attempts_strict(
835        &self,
836        value: &Value,
837        path: &str,
838    ) -> Result<(), SecurityValidationError> {
839        match value {
840            Value::String(s) => {
841                // More aggressive injection detection for anonymous users
842                let violations = self.sanitizer.detect_injection(s);
843
844                // Additional checks for anonymous users
845                let suspicious_patterns = [
846                    "eval", "exec", "system", "cmd", "shell", "script", "import", "require",
847                    "include", "load",
848                ];
849
850                let lower_s = s.to_lowercase();
851                for pattern in &suspicious_patterns {
852                    if lower_s.contains(pattern) {
853                        self.log_violation(SecurityViolation {
854                            violation_type: SecurityViolationType::InjectionAttempt,
855                            severity: SecuritySeverity::Critical,
856                            description: format!(
857                                "Suspicious pattern '{}' detected in anonymous user request",
858                                pattern
859                            ),
860                            field: Some(path.to_string()),
861                            value: Some(s.clone()),
862                            timestamp: chrono::Utc::now(),
863                        });
864
865                        return Err(SecurityValidationError::InjectionDetected {
866                            param: path.to_string(),
867                        });
868                    }
869                }
870
871                if !violations.is_empty() {
872                    self.log_violation(SecurityViolation {
873                        violation_type: SecurityViolationType::InjectionAttempt,
874                        severity: SecuritySeverity::Critical,
875                        description: format!(
876                            "Enhanced injection detection: {}",
877                            violations.join(", ")
878                        ),
879                        field: Some(path.to_string()),
880                        value: Some(s.clone()),
881                        timestamp: chrono::Utc::now(),
882                    });
883
884                    return Err(SecurityValidationError::InjectionDetected {
885                        param: path.to_string(),
886                    });
887                }
888            }
889            Value::Object(obj) => {
890                for (key, val) in obj {
891                    let new_path = format!("{}.{}", path, key);
892                    self.detect_injection_attempts_strict(val, &new_path)?;
893                }
894            }
895            Value::Array(arr) => {
896                for (i, val) in arr.iter().enumerate() {
897                    let new_path = format!("{}[{}]", path, i);
898                    self.detect_injection_attempts_strict(val, &new_path)?;
899                }
900            }
901            _ => {} // Other types are safe
902        }
903
904        Ok(())
905    }
906}
907
908/// Helper for creating security configurations
909impl RequestSecurityConfig {
910    /// Create a permissive configuration (minimal validation)
911    pub fn permissive() -> Self {
912        Self {
913            enabled: true,
914            limits: RequestLimitsConfig {
915                max_request_size: 100 * 1024 * 1024, // 100MB
916                max_parameters: 1000,
917                max_parameter_size: 10 * 1024 * 1024, // 10MB
918                max_string_length: 100_000,
919                max_array_length: 10_000,
920                max_object_depth: 20,
921                max_object_keys: 1000,
922            },
923            enable_injection_detection: false,
924            enable_sanitization: false,
925            allowed_methods: HashSet::new(),
926            blocked_methods: HashSet::new(),
927            enable_method_rate_limiting: false,
928            method_rate_limits: HashMap::new(),
929            log_violations: true,
930            fail_on_violations: false,
931        }
932    }
933
934    /// Create a strict configuration (maximum security)
935    pub fn strict() -> Self {
936        let mut blocked_methods = HashSet::new();
937        blocked_methods.insert("logging/setLevel".to_string()); // Admin only
938
939        Self {
940            enabled: true,
941            limits: RequestLimitsConfig {
942                max_request_size: 1024 * 1024, // 1MB
943                max_parameters: 50,
944                max_parameter_size: 100 * 1024, // 100KB
945                max_string_length: 1000,
946                max_array_length: 100,
947                max_object_depth: 5,
948                max_object_keys: 20,
949            },
950            enable_injection_detection: true,
951            enable_sanitization: true,
952            allowed_methods: HashSet::new(),
953            blocked_methods,
954            enable_method_rate_limiting: true,
955            method_rate_limits: {
956                let mut limits = HashMap::new();
957                limits.insert("tools/call".to_string(), 30); // 0.5 per second
958                limits.insert("resources/read".to_string(), 60); // 1 per second
959                limits
960            },
961            log_violations: true,
962            fail_on_violations: true,
963        }
964    }
965}
966
967#[cfg(test)]
968mod tests {
969    use super::*;
970
971    #[test]
972    fn test_security_severity_ordering() {
973        assert!(SecuritySeverity::Low < SecuritySeverity::Medium);
974        assert!(SecuritySeverity::Medium < SecuritySeverity::High);
975        assert!(SecuritySeverity::High < SecuritySeverity::Critical);
976    }
977}