kindly_guard_server/
config.rs

1// Copyright 2025 Kindly Software Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//! Configuration for `KindlyGuard`
15//!
16//! This module defines all security-related configuration structures for KindlyGuard.
17//! Each configuration option has been carefully designed with security implications in mind.
18//!
19//! # Security Principles
20//!
21//! 1. **Secure Defaults**: All configuration defaults err on the side of security
22//! 2. **Defense in Depth**: Multiple layers of security can be configured independently
23//! 3. **Least Privilege**: Features are disabled by default and must be explicitly enabled
24//! 4. **Transparency**: Security implications of each setting are clearly documented
25//!
26//! # Configuration Hierarchy
27//!
28//! Configuration sources are checked in order:
29//! 1. Environment variable `KINDLY_GUARD_CONFIG` (highest precedence)
30//! 2. `kindly-guard.toml` in current directory
31//! 3. Built-in secure defaults (lowest precedence)
32
33use anyhow::Result;
34use base64::{engine::general_purpose, Engine as _};
35use serde::{Deserialize, Serialize};
36use std::path::PathBuf;
37
38pub mod reload;
39
40use crate::audit::AuditConfig;
41use crate::auth::AuthConfig;
42#[cfg(feature = "enhanced")]
43use crate::event_processor::EventProcessorConfig;
44use crate::neutralizer::NeutralizationConfig;
45use crate::plugins::PluginConfig;
46use crate::rate_limit::RateLimitConfig;
47use crate::resilience::config::ResilienceConfig;
48use crate::signing::SigningConfig;
49use crate::storage::StorageConfig;
50use crate::telemetry::TelemetryConfig;
51use crate::transport::TransportConfig;
52
53// Stub EventProcessorConfig when enhanced feature is not enabled
54#[cfg(not(feature = "enhanced"))]
55#[derive(Debug, Clone, Serialize, Deserialize, Default)]
56pub struct EventProcessorConfig {
57    pub enabled: bool,
58}
59
60/// Main configuration structure for KindlyGuard
61///
62/// # Security Architecture
63///
64/// KindlyGuard's configuration implements defense-in-depth with multiple
65/// security layers that work together:
66///
67/// 1. **Authentication** (`auth`) - Identity verification and access control
68/// 2. **Rate Limiting** (`rate_limit`) - Abuse and DoS prevention
69/// 3. **Scanner** (`scanner`) - Threat detection and analysis
70/// 4. **Neutralization** (`neutralization`) - Threat remediation
71/// 5. **Audit** (`audit`) - Security event logging and compliance
72///
73/// # Configuration Priority
74///
75/// When multiple security features could conflict:
76/// 1. Authentication failures block everything (highest priority)
77/// 2. Rate limits apply after authentication
78/// 3. Scanner runs on all authenticated requests
79/// 4. Neutralization only acts on detected threats
80///
81/// # Example: Minimum Secure Configuration
82///
83/// ```toml
84/// [auth]
85/// enabled = true
86/// jwt_secret = "your-base64-encoded-secret"
87/// trusted_issuers = ["https://your-auth-server.com"]
88///
89/// [rate_limit]
90/// enabled = true
91/// default_rpm = 60
92///
93/// [scanner]
94/// unicode_detection = true
95/// injection_detection = true
96/// path_traversal_detection = true
97/// xss_detection = true
98///
99/// [neutralization]
100/// mode = "automatic"
101/// audit_all_actions = true
102/// ```
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct Config {
105    /// Server configuration
106    ///
107    /// Controls network exposure and connection handling.
108    /// Lower limits = more secure but less scalable.
109    pub server: ServerConfig,
110
111    /// Security scanning configuration
112    ///
113    /// Primary defense against malicious input.
114    /// More detections enabled = better security coverage.
115    pub scanner: ScannerConfig,
116
117    /// Shield display configuration
118    ///
119    /// Visual security status indicator.
120    /// No direct security impact but aids monitoring.
121    pub shield: ShieldConfig,
122
123    /// Authentication configuration
124    ///
125    /// Access control and identity verification.
126    /// MUST be enabled in production environments.
127    pub auth: AuthConfig,
128
129    /// Message signing configuration
130    ///
131    /// Cryptographic integrity for requests/responses.
132    /// Prevents tampering and replay attacks.
133    pub signing: SigningConfig,
134
135    /// Rate limiting configuration
136    ///
137    /// Prevents abuse and resource exhaustion.
138    /// Essential for public-facing deployments.
139    pub rate_limit: RateLimitConfig,
140
141    /// Enhanced security event processing configuration
142    ///
143    /// Advanced threat correlation and analysis.
144    /// Provides deeper security insights when enabled.
145    pub event_processor: EventProcessorConfig,
146
147    /// Telemetry configuration
148    ///
149    /// Security monitoring and metrics.
150    /// Critical for detecting attacks and anomalies.
151    pub telemetry: TelemetryConfig,
152
153    /// Storage configuration
154    ///
155    /// Secure storage for backups and audit logs.
156    /// Encryption and access control are essential.
157    pub storage: StorageConfig,
158
159    /// Plugin system configuration
160    ///
161    /// Extensibility with security boundaries.
162    /// Only load trusted, signed plugins.
163    pub plugins: PluginConfig,
164
165    /// Audit logging configuration
166    ///
167    /// Forensic trail of all security events.
168    /// Required for compliance and incident response.
169    pub audit: AuditConfig,
170
171    /// Transport layer configuration
172    ///
173    /// Communication security settings.
174    /// Use TLS for all network transports.
175    pub transport: TransportConfig,
176
177    /// Resilience configuration for circuit breakers and retry
178    ///
179    /// Prevents cascading failures under attack.
180    /// Maintains availability during security incidents.
181    pub resilience: ResilienceConfig,
182
183    /// Threat neutralization configuration
184    ///
185    /// Active threat remediation settings.
186    /// Transforms malicious input into safe content.
187    pub neutralization: NeutralizationConfig,
188
189    /// Neutralizer configuration (alias for neutralization)
190    ///
191    /// Some tests expect this field name.
192    /// This is an alias for backwards compatibility.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub neutralizer: Option<NeutralizationConfig>,
195}
196
197/// Server configuration controlling network and connection settings
198///
199/// # Security Implications
200///
201/// The server configuration affects the attack surface and resource consumption:
202/// - Lower connection limits prevent resource exhaustion attacks
203/// - Shorter timeouts reduce the window for slow loris attacks
204/// - stdio mode is more secure than network modes (no network exposure)
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct ServerConfig {
207    /// Port to listen on (for HTTP transport)
208    ///
209    /// **Default**: 8080
210    /// **Security**: Use non-standard ports to reduce automated scanning.
211    /// Consider using a reverse proxy for production deployments.
212    /// **Range**: 1-65535 (ports < 1024 require root/admin privileges)
213    #[serde(default = "default_port")]
214    pub port: u16,
215
216    /// Enable stdio transport (default for MCP)
217    ///
218    /// **Default**: true (secure by default)
219    /// **Security**: stdio is the most secure transport as it doesn't expose
220    /// a network interface. Recommended for local integrations.
221    /// **Trade-off**: Limited to local process communication only
222    #[serde(default = "default_true")]
223    pub stdio: bool,
224
225    /// Maximum concurrent connections
226    ///
227    /// **Default**: 100
228    /// **Security**: Prevents resource exhaustion attacks. Lower values are more
229    /// secure but may impact legitimate high-traffic scenarios.
230    /// **Range**: 1-10000 (recommend 10-500 for most deployments)
231    /// **Warning**: Values > 1000 may cause memory issues under attack
232    #[serde(default = "default_max_connections")]
233    pub max_connections: usize,
234
235    /// Request timeout in seconds
236    ///
237    /// **Default**: 30 seconds
238    /// **Security**: Shorter timeouts prevent slow loris and resource holding attacks.
239    /// Longer timeouts may be needed for complex operations.
240    /// **Range**: 1-300 (recommend 10-60 for most use cases)
241    /// **Trade-off**: Too short may interrupt legitimate long operations
242    #[serde(default = "default_timeout")]
243    pub request_timeout_secs: u64,
244}
245
246/// Scanner configuration for threat detection settings
247///
248/// # Security Implications
249///
250/// The scanner is your first line of defense against malicious input:
251/// - Disabling any detection reduces security coverage
252/// - Custom patterns can detect organization-specific threats
253/// - Scan depth limits prevent algorithmic complexity attacks
254/// - Enhanced mode provides better detection at a performance cost
255///
256/// # Example: Secure Production Configuration
257///
258/// ```toml
259/// [scanner]
260/// unicode_detection = true      # Detect unicode attacks
261/// injection_detection = true    # Detect SQL/command injection
262/// path_traversal_detection = true  # Detect directory traversal
263/// xss_detection = true         # Detect XSS attempts
264/// enhanced_mode = true         # Maximum security (if available)
265/// max_scan_depth = 20          # Deep scanning for nested payloads
266/// custom_patterns = "/etc/kindly-guard/patterns.toml"
267/// ```
268#[derive(Debug, Clone, Serialize, Deserialize)]
269pub struct ScannerConfig {
270    /// Enable unicode threat detection
271    ///
272    /// **Default**: true (secure by default)
273    /// **Security**: Detects BiDi overrides, zero-width chars, homoglyphs.
274    /// Essential for preventing unicode-based attacks and spoofing.
275    /// **Warning**: Disabling exposes you to text direction manipulation
276    #[serde(default = "default_true")]
277    pub unicode_detection: bool,
278
279    /// Enable injection detection
280    ///
281    /// **Default**: true (secure by default)
282    /// **Security**: Detects SQL, NoSQL, command, and LDAP injection attempts.
283    /// Critical for preventing code execution and data breaches.
284    /// **Coverage**: SQL, shell commands, LDAP queries, NoSQL operations
285    #[serde(default = "default_true")]
286    pub injection_detection: bool,
287
288    /// Enable path traversal detection
289    ///
290    /// **Default**: true (secure by default)
291    /// **Security**: Detects attempts to access files outside intended directories.
292    /// Prevents unauthorized file access and directory listing.
293    /// **Patterns**: ../, ..\, absolute paths, URL encoding variants
294    #[serde(default = "default_true")]
295    pub path_traversal_detection: bool,
296
297    /// Enable XSS detection
298    ///
299    /// **Default**: Some(true) (secure by default)
300    /// **Security**: Detects cross-site scripting attempts in various contexts.
301    /// Essential for web-facing applications and APIs.
302    /// **Coverage**: Script tags, event handlers, data URIs, SVG attacks
303    #[serde(default = "default_some_true")]
304    pub xss_detection: Option<bool>,
305
306    /// Enable cryptographic security detection
307    ///
308    /// **Default**: true (secure by default)
309    /// **Security**: Detects weak cryptographic patterns and insecure implementations.
310    /// Critical for preventing cryptographic vulnerabilities and data exposure.
311    /// **Coverage**: Deprecated algorithms (MD5, SHA1, DES), weak keys, insecure RNG, bad KDF
312    /// **2025 Standards**: Enforces current NIST recommendations for key sizes and algorithms
313    #[serde(default = "default_true")]
314    pub crypto_detection: bool,
315
316    /// Enable enhanced mode for scanners (uses advanced algorithms when available)
317    ///
318    /// **Default**: Some(false) (standard mode)
319    /// **Security**: Enhanced mode provides deeper analysis and pattern correlation.
320    /// Better detection accuracy at the cost of some performance.
321    /// **Trade-off**: 10-20% performance impact for 50%+ better detection
322    #[serde(default = "default_some_false")]
323    pub enhanced_mode: Option<bool>,
324
325    /// Custom threat patterns file
326    ///
327    /// **Default**: None
328    /// **Security**: Add organization-specific threat patterns.
329    /// Useful for detecting internal security policies violations.
330    /// **Format**: TOML file with regex patterns and metadata
331    /// **Example**: `/etc/kindly-guard/custom-patterns.toml`
332    pub custom_patterns: Option<PathBuf>,
333
334    /// Maximum scan depth for nested structures
335    ///
336    /// **Default**: 10
337    /// **Security**: Prevents algorithmic complexity attacks through deep nesting.
338    /// Lower values are more secure but may miss deeply nested threats.
339    /// **Range**: 1-100 (recommend 5-20 for most use cases)
340    /// **Warning**: Values > 50 may cause performance issues
341    #[serde(default = "default_max_depth")]
342    pub max_scan_depth: usize,
343
344    /// Enable high-performance event buffer
345    ///
346    /// **Default**: false (standard mode)
347    /// **Security**: Enables advanced correlation and pattern matching.
348    /// Provides "purple shield" mode with enhanced threat detection.
349    /// **Requirements**: Additional memory (10-50MB depending on load)
350    #[serde(default = "default_false")]
351    pub enable_event_buffer: bool,
352
353    /// Maximum content size to scan (in bytes)
354    ///
355    /// **Default**: 5MB (5,242,880 bytes)
356    /// **Security**: Prevents DoS attacks through large payload scanning.
357    /// Content larger than this will be rejected with a DosPotential threat.
358    /// **Range**: 1KB-100MB (recommend 1-10MB for most use cases)
359    /// **Trade-off**: Larger values allow bigger legitimate payloads but increase DoS risk
360    #[serde(default = "default_max_content_size")]
361    pub max_content_size: usize,
362
363    /// Maximum input size to scan (alias for max_content_size)
364    ///
365    /// **Default**: Uses max_content_size value
366    /// **Security**: Some tests expect this field name.
367    /// This is an alias for backwards compatibility.
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub max_input_size: Option<usize>,
370}
371
372/// Shield display configuration
373///
374/// The shield provides visual feedback about security status:
375/// - 🟢 Green: Normal operation, no threats
376/// - 🟣 Purple: Enhanced mode active (better detection)
377/// - 🔴 Red: Active threat detected
378/// - âš« Gray: Disabled or error state
379///
380/// While not a security feature itself, the shield aids in:
381/// - Real-time threat awareness
382/// - System health monitoring
383/// - Security posture visualization
384#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct ShieldConfig {
386    /// Enable shield display
387    ///
388    /// **Default**: false (no visual output)
389    /// **Security**: No direct impact, but helps operators monitor security status.
390    /// Useful for development and attended deployments.
391    #[serde(default = "default_false")]
392    pub enabled: bool,
393
394    /// Update interval in milliseconds
395    ///
396    /// **Default**: 1000ms (1 second)
397    /// **Performance**: Lower values provide more responsive feedback but use more CPU.
398    /// **Range**: 100-10000ms (recommend 500-2000ms)
399    #[serde(default = "default_update_interval")]
400    pub update_interval_ms: u64,
401
402    /// Show detailed statistics
403    ///
404    /// **Default**: false (basic display only)
405    /// **Security**: Shows threat counts, types, and neutralization stats.
406    /// Helpful for understanding attack patterns in real-time.
407    #[serde(default = "default_false")]
408    pub detailed_stats: bool,
409
410    /// Enable color output
411    ///
412    /// **Default**: true (colored output)
413    /// **Accessibility**: Set to false for screen readers or monochrome terminals.
414    /// Colors help quickly identify security state changes.
415    #[serde(default = "default_true")]
416    pub color: bool,
417}
418
419impl Config {
420    /// Check if event processor is enabled
421    pub const fn is_event_processor_enabled(&self) -> bool {
422        #[cfg(feature = "enhanced")]
423        return self.event_processor.enabled;
424        #[cfg(not(feature = "enhanced"))]
425        return false;
426    }
427
428    /// Get neutralizer configuration
429    /// Returns the neutralizer field if set, otherwise returns neutralization
430    pub fn neutralizer(&self) -> &NeutralizationConfig {
431        self.neutralizer.as_ref().unwrap_or(&self.neutralization)
432    }
433
434    /// Load configuration from environment and files
435    ///
436    /// # Security Notes
437    ///
438    /// - Configuration files should have restricted permissions (600 or 640)
439    /// - Never store secrets directly in config files - use environment variables
440    /// - Validate all loaded configurations before use
441    /// - Default configuration is intentionally conservative for security
442    pub fn load() -> Result<Self> {
443        // First, try to load from config file
444        let config_path = std::env::var("KINDLY_GUARD_CONFIG")
445            .map_or_else(|_| PathBuf::from("kindly-guard.toml"), PathBuf::from);
446
447        if config_path.exists() {
448            Self::load_from_file(&config_path.to_string_lossy())
449        } else {
450            // Use default configuration
451            Ok(Self::default())
452        }
453    }
454
455    /// Load configuration from a specific file
456    ///
457    /// # Security Warning
458    ///
459    /// Ensure the configuration file is from a trusted source and has
460    /// appropriate file permissions to prevent unauthorized modifications.
461    pub fn load_from_file(path: &str) -> Result<Self> {
462        let content = std::fs::read_to_string(path)?;
463        let config: Self = toml::from_str(&content)?;
464        Ok(config)
465    }
466
467    /// Validate configuration for security best practices
468    ///
469    /// # Example
470    ///
471    /// ```
472    /// let config = Config::load()?;
473    /// config.validate_security()?;
474    /// ```
475    pub fn validate_security(&self) -> Result<()> {
476        // Warn if authentication is disabled
477        if !self.auth.enabled {
478            tracing::warn!("Authentication is disabled - this is insecure for production!");
479        }
480
481        // Warn if rate limiting is disabled
482        if !self.rate_limit.enabled {
483            tracing::warn!("Rate limiting is disabled - vulnerable to DoS attacks!");
484        }
485
486        // Check for weak JWT secrets
487        if let Some(ref secret) = self.auth.jwt_secret {
488            let decoded = general_purpose::STANDARD.decode(secret)?;
489            if decoded.len() < 32 {
490                return Err(anyhow::anyhow!(
491                    "JWT secret too short - use at least 256 bits (32 bytes)"
492                ));
493            }
494        }
495
496        // Ensure scanner is properly configured
497        if !self.scanner.unicode_detection
498            || !self.scanner.injection_detection
499            || !self.scanner.path_traversal_detection
500        {
501            tracing::warn!("Some threat detections are disabled - reduced security coverage");
502        }
503
504        Ok(())
505    }
506}
507
508impl Default for Config {
509    fn default() -> Self {
510        Self {
511            server: ServerConfig {
512                port: default_port(),
513                stdio: default_true(),
514                max_connections: default_max_connections(),
515                request_timeout_secs: default_timeout(),
516            },
517            scanner: ScannerConfig {
518                unicode_detection: default_true(),
519                injection_detection: default_true(),
520                path_traversal_detection: default_true(),
521                xss_detection: Some(true),
522                crypto_detection: default_true(),
523                enhanced_mode: Some(false),
524                custom_patterns: None,
525                max_scan_depth: default_max_depth(),
526                enable_event_buffer: default_false(),
527                max_content_size: default_max_content_size(),
528                max_input_size: None,
529            },
530            shield: ShieldConfig {
531                enabled: default_false(),
532                update_interval_ms: default_update_interval(),
533                detailed_stats: default_false(),
534                color: default_true(),
535            },
536            auth: AuthConfig::default(),
537            signing: SigningConfig::default(),
538            rate_limit: RateLimitConfig::default(),
539            event_processor: EventProcessorConfig::default(),
540            telemetry: TelemetryConfig::default(),
541            storage: StorageConfig::default(),
542            plugins: PluginConfig::default(),
543            audit: AuditConfig::default(),
544            transport: TransportConfig::default(),
545            resilience: ResilienceConfig::default(),
546            neutralization: NeutralizationConfig::default(),
547            neutralizer: None,
548        }
549    }
550}
551
552impl Default for ShieldConfig {
553    fn default() -> Self {
554        Self {
555            enabled: default_false(),
556            update_interval_ms: default_update_interval(),
557            detailed_stats: default_false(),
558            color: default_true(),
559        }
560    }
561}
562
563impl Default for ServerConfig {
564    fn default() -> Self {
565        Self {
566            port: default_port(),
567            stdio: default_true(),
568            max_connections: default_max_connections(),
569            request_timeout_secs: default_timeout(),
570        }
571    }
572}
573
574impl Default for ScannerConfig {
575    fn default() -> Self {
576        Self {
577            unicode_detection: default_true(),
578            injection_detection: default_true(),
579            path_traversal_detection: default_true(),
580            xss_detection: default_some_true(),
581            crypto_detection: default_true(),
582            enhanced_mode: default_some_false(),
583            custom_patterns: None,
584            max_scan_depth: default_max_depth(),
585            enable_event_buffer: default_false(),
586            max_content_size: default_max_content_size(),
587            max_input_size: None,
588        }
589    }
590}
591
592// Default value functions
593const fn default_port() -> u16 {
594    8080
595}
596const fn default_true() -> bool {
597    true
598}
599const fn default_false() -> bool {
600    false
601}
602fn default_some_true() -> Option<bool> {
603    Some(true)
604}
605fn default_some_false() -> Option<bool> {
606    Some(false)
607}
608const fn default_max_connections() -> usize {
609    100
610}
611const fn default_max_depth() -> usize {
612    10
613}
614const fn default_update_interval() -> u64 {
615    1000
616}
617const fn default_timeout() -> u64 {
618    30
619}
620const fn default_max_content_size() -> usize {
621    5 * 1024 * 1024 // 5MB
622}
623
624/// Example secure production configuration
625///
626/// Save this as `kindly-guard.toml` and adjust for your environment:
627///
628/// ```toml
629/// # Server Configuration
630/// [server]
631/// port = 8443                    # Use HTTPS port
632/// stdio = false                  # Network mode for production
633/// max_connections = 500          # Adjust based on load
634/// request_timeout_secs = 30      # Prevent slow loris attacks
635///
636/// # Authentication - REQUIRED for production
637/// [auth]
638/// enabled = true
639/// validation_endpoint = "https://auth.example.com/oauth2/introspect"
640/// trusted_issuers = ["https://auth.example.com"]
641/// cache_ttl_seconds = 300
642/// validate_resource_indicators = true
643/// jwt_secret = "YOUR-BASE64-ENCODED-256-BIT-SECRET"  # Generate with: openssl rand -base64 32
644/// require_signature_verification = true
645///
646/// [auth.required_scopes]
647/// default = ["kindlyguard:access"]
648///
649/// [auth.required_scopes.tools]
650/// "scan" = ["security:read"]
651/// "neutralize" = ["security:write"]
652///
653/// # Rate Limiting - Essential for DoS protection
654/// [rate_limit]
655/// enabled = true
656/// default_rpm = 60
657/// burst_capacity = 10
658/// cleanup_interval_secs = 300
659/// adaptive = true
660/// threat_penalty_multiplier = 0.5
661///
662/// [rate_limit.method_limits]
663/// "tools/list" = { rpm = 120, burst = 20 }
664/// "tools/call" = { rpm = 30, burst = 5 }
665/// "security/threats" = { rpm = 10, burst = 2 }
666///
667/// # Scanner - Core threat detection
668/// [scanner]
669/// unicode_detection = true
670/// injection_detection = true
671/// path_traversal_detection = true
672/// xss_detection = true
673/// enhanced_mode = true          # If available
674/// max_scan_depth = 20           # Deep scanning
675/// enable_event_buffer = true    # Purple shield mode
676/// custom_patterns = "/etc/kindly-guard/patterns.toml"
677///
678/// # Neutralization - Active threat remediation
679/// [neutralization]
680/// mode = "automatic"
681/// backup_originals = true
682/// audit_all_actions = true
683///
684/// [neutralization.unicode]
685/// bidi_replacement = "marker"
686/// zero_width_action = "remove"
687/// homograph_action = "ascii"
688///
689/// [neutralization.injection]
690/// sql_action = "parameterize"
691/// command_action = "escape"
692/// path_action = "normalize"
693/// prompt_action = "wrap"
694///
695/// # Audit - Security event logging
696/// [audit]
697/// enabled = true
698/// file_path = "/var/log/kindly-guard/audit.log"
699/// max_file_size_mb = 100
700/// max_files = 10
701/// include_request_body = true
702/// include_response_body = false
703///
704/// # Shield Display
705/// [shield]
706/// enabled = true
707/// update_interval_ms = 1000
708/// detailed_stats = true
709/// color = true
710/// ```
711
712#[cfg(test)]
713mod tests {
714    use super::*;
715
716    #[test]
717    fn test_default_config() {
718        let config = Config::default();
719        assert!(config.server.stdio);
720        assert!(config.scanner.unicode_detection);
721        assert_eq!(config.server.port, 8080);
722    }
723
724    #[test]
725    fn test_security_validation() {
726        let mut config = Config::default();
727
728        // Should warn but not error with default config
729        assert!(config.validate_security().is_ok());
730
731        // Should error with weak JWT secret
732        config.auth.jwt_secret = Some("c2hvcnQ=".to_string()); // "short" in base64
733        assert!(config.validate_security().is_err());
734    }
735}