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    /// Allow common text control characters (newline, tab, carriage return)
372    ///
373    /// **Default**: false (strict mode - all control chars are dangerous)
374    /// **Security**: When true, allows \n, \r, \t in text without flagging as threats.
375    /// Useful for wrapping CLI tools that process text documents.
376    /// **Warning**: Only enable for trusted input sources like CLI wrapping.
377    #[serde(default = "default_false")]
378    pub allow_text_control_chars: bool,
379}
380
381/// Shield display configuration
382///
383/// The shield provides visual feedback about security status:
384/// - 🟢 Green: Normal operation, no threats
385/// - 🟣 Purple: Enhanced mode active (better detection)
386/// - 🔴 Red: Active threat detected
387/// - âš« Gray: Disabled or error state
388///
389/// While not a security feature itself, the shield aids in:
390/// - Real-time threat awareness
391/// - System health monitoring
392/// - Security posture visualization
393#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct ShieldConfig {
395    /// Enable shield display
396    ///
397    /// **Default**: false (no visual output)
398    /// **Security**: No direct impact, but helps operators monitor security status.
399    /// Useful for development and attended deployments.
400    #[serde(default = "default_false")]
401    pub enabled: bool,
402
403    /// Update interval in milliseconds
404    ///
405    /// **Default**: 1000ms (1 second)
406    /// **Performance**: Lower values provide more responsive feedback but use more CPU.
407    /// **Range**: 100-10000ms (recommend 500-2000ms)
408    #[serde(default = "default_update_interval")]
409    pub update_interval_ms: u64,
410
411    /// Show detailed statistics
412    ///
413    /// **Default**: false (basic display only)
414    /// **Security**: Shows threat counts, types, and neutralization stats.
415    /// Helpful for understanding attack patterns in real-time.
416    #[serde(default = "default_false")]
417    pub detailed_stats: bool,
418
419    /// Enable color output
420    ///
421    /// **Default**: true (colored output)
422    /// **Accessibility**: Set to false for screen readers or monochrome terminals.
423    /// Colors help quickly identify security state changes.
424    #[serde(default = "default_true")]
425    pub color: bool,
426}
427
428impl Config {
429    /// Check if event processor is enabled
430    pub const fn is_event_processor_enabled(&self) -> bool {
431        #[cfg(feature = "enhanced")]
432        return self.event_processor.enabled;
433        #[cfg(not(feature = "enhanced"))]
434        return false;
435    }
436
437    /// Get neutralizer configuration
438    /// Returns the neutralizer field if set, otherwise returns neutralization
439    pub fn neutralizer(&self) -> &NeutralizationConfig {
440        self.neutralizer.as_ref().unwrap_or(&self.neutralization)
441    }
442
443    /// Load configuration from environment and files
444    ///
445    /// # Security Notes
446    ///
447    /// - Configuration files should have restricted permissions (600 or 640)
448    /// - Never store secrets directly in config files - use environment variables
449    /// - Validate all loaded configurations before use
450    /// - Default configuration is intentionally conservative for security
451    pub fn load() -> Result<Self> {
452        // First, try to load from config file
453        let config_path = std::env::var("KINDLY_GUARD_CONFIG")
454            .map_or_else(|_| PathBuf::from("kindly-guard.toml"), PathBuf::from);
455
456        if config_path.exists() {
457            Self::load_from_file(&config_path.to_string_lossy())
458        } else {
459            // Use default configuration
460            Ok(Self::default())
461        }
462    }
463
464    /// Load configuration from a specific file
465    ///
466    /// # Security Warning
467    ///
468    /// Ensure the configuration file is from a trusted source and has
469    /// appropriate file permissions to prevent unauthorized modifications.
470    pub fn load_from_file(path: &str) -> Result<Self> {
471        let content = std::fs::read_to_string(path)?;
472        let config: Self = toml::from_str(&content)?;
473        Ok(config)
474    }
475
476    /// Validate configuration for security best practices
477    ///
478    /// # Example
479    ///
480    /// ```
481    /// let config = Config::load()?;
482    /// config.validate_security()?;
483    /// ```
484    pub fn validate_security(&self) -> Result<()> {
485        // Warn if authentication is disabled
486        if !self.auth.enabled {
487            tracing::warn!("Authentication is disabled - this is insecure for production!");
488        }
489
490        // Warn if rate limiting is disabled
491        if !self.rate_limit.enabled {
492            tracing::warn!("Rate limiting is disabled - vulnerable to DoS attacks!");
493        }
494
495        // Check for weak JWT secrets
496        if let Some(ref secret) = self.auth.jwt_secret {
497            let decoded = general_purpose::STANDARD.decode(secret)?;
498            if decoded.len() < 32 {
499                return Err(anyhow::anyhow!(
500                    "JWT secret too short - use at least 256 bits (32 bytes)"
501                ));
502            }
503        }
504
505        // Ensure scanner is properly configured
506        if !self.scanner.unicode_detection
507            || !self.scanner.injection_detection
508            || !self.scanner.path_traversal_detection
509        {
510            tracing::warn!("Some threat detections are disabled - reduced security coverage");
511        }
512
513        Ok(())
514    }
515}
516
517impl Default for Config {
518    fn default() -> Self {
519        Self {
520            server: ServerConfig {
521                port: default_port(),
522                stdio: default_true(),
523                max_connections: default_max_connections(),
524                request_timeout_secs: default_timeout(),
525            },
526            scanner: ScannerConfig {
527                unicode_detection: default_true(),
528                injection_detection: default_true(),
529                path_traversal_detection: default_true(),
530                xss_detection: Some(true),
531                crypto_detection: default_true(),
532                enhanced_mode: Some(false),
533                custom_patterns: None,
534                max_scan_depth: default_max_depth(),
535                enable_event_buffer: default_false(),
536                max_content_size: default_max_content_size(),
537                max_input_size: None,
538                allow_text_control_chars: default_false(),
539            },
540            shield: ShieldConfig {
541                enabled: default_false(),
542                update_interval_ms: default_update_interval(),
543                detailed_stats: default_false(),
544                color: default_true(),
545            },
546            auth: AuthConfig::default(),
547            signing: SigningConfig::default(),
548            rate_limit: RateLimitConfig::default(),
549            event_processor: EventProcessorConfig::default(),
550            telemetry: TelemetryConfig::default(),
551            storage: StorageConfig::default(),
552            plugins: PluginConfig::default(),
553            audit: AuditConfig::default(),
554            transport: TransportConfig::default(),
555            resilience: ResilienceConfig::default(),
556            neutralization: NeutralizationConfig::default(),
557            neutralizer: None,
558        }
559    }
560}
561
562impl Default for ShieldConfig {
563    fn default() -> Self {
564        Self {
565            enabled: default_false(),
566            update_interval_ms: default_update_interval(),
567            detailed_stats: default_false(),
568            color: default_true(),
569        }
570    }
571}
572
573impl Default for ServerConfig {
574    fn default() -> Self {
575        Self {
576            port: default_port(),
577            stdio: default_true(),
578            max_connections: default_max_connections(),
579            request_timeout_secs: default_timeout(),
580        }
581    }
582}
583
584impl Default for ScannerConfig {
585    fn default() -> Self {
586        Self {
587            unicode_detection: default_true(),
588            injection_detection: default_true(),
589            path_traversal_detection: default_true(),
590            xss_detection: default_some_true(),
591            crypto_detection: default_true(),
592            enhanced_mode: default_some_false(),
593            custom_patterns: None,
594            max_scan_depth: default_max_depth(),
595            enable_event_buffer: default_false(),
596            max_content_size: default_max_content_size(),
597            max_input_size: None,
598            allow_text_control_chars: default_false(),
599        }
600    }
601}
602
603// Default value functions
604const fn default_port() -> u16 {
605    8080
606}
607const fn default_true() -> bool {
608    true
609}
610const fn default_false() -> bool {
611    false
612}
613fn default_some_true() -> Option<bool> {
614    Some(true)
615}
616fn default_some_false() -> Option<bool> {
617    Some(false)
618}
619const fn default_max_connections() -> usize {
620    100
621}
622const fn default_max_depth() -> usize {
623    10
624}
625const fn default_update_interval() -> u64 {
626    1000
627}
628const fn default_timeout() -> u64 {
629    30
630}
631const fn default_max_content_size() -> usize {
632    5 * 1024 * 1024 // 5MB
633}
634
635/// Example secure production configuration
636///
637/// Save this as `kindly-guard.toml` and adjust for your environment:
638///
639/// ```toml
640/// # Server Configuration
641/// [server]
642/// port = 8443                    # Use HTTPS port
643/// stdio = false                  # Network mode for production
644/// max_connections = 500          # Adjust based on load
645/// request_timeout_secs = 30      # Prevent slow loris attacks
646///
647/// # Authentication - REQUIRED for production
648/// [auth]
649/// enabled = true
650/// validation_endpoint = "https://auth.example.com/oauth2/introspect"
651/// trusted_issuers = ["https://auth.example.com"]
652/// cache_ttl_seconds = 300
653/// validate_resource_indicators = true
654/// jwt_secret = "YOUR-BASE64-ENCODED-256-BIT-SECRET"  # Generate with: openssl rand -base64 32
655/// require_signature_verification = true
656///
657/// [auth.required_scopes]
658/// default = ["kindlyguard:access"]
659///
660/// [auth.required_scopes.tools]
661/// "scan" = ["security:read"]
662/// "neutralize" = ["security:write"]
663///
664/// # Rate Limiting - Essential for DoS protection
665/// [rate_limit]
666/// enabled = true
667/// default_rpm = 60
668/// burst_capacity = 10
669/// cleanup_interval_secs = 300
670/// adaptive = true
671/// threat_penalty_multiplier = 0.5
672///
673/// [rate_limit.method_limits]
674/// "tools/list" = { rpm = 120, burst = 20 }
675/// "tools/call" = { rpm = 30, burst = 5 }
676/// "security/threats" = { rpm = 10, burst = 2 }
677///
678/// # Scanner - Core threat detection
679/// [scanner]
680/// unicode_detection = true
681/// injection_detection = true
682/// path_traversal_detection = true
683/// xss_detection = true
684/// enhanced_mode = true          # If available
685/// max_scan_depth = 20           # Deep scanning
686/// enable_event_buffer = true    # Purple shield mode
687/// custom_patterns = "/etc/kindly-guard/patterns.toml"
688///
689/// # Neutralization - Active threat remediation
690/// [neutralization]
691/// mode = "automatic"
692/// backup_originals = true
693/// audit_all_actions = true
694///
695/// [neutralization.unicode]
696/// bidi_replacement = "marker"
697/// zero_width_action = "remove"
698/// homograph_action = "ascii"
699///
700/// [neutralization.injection]
701/// sql_action = "parameterize"
702/// command_action = "escape"
703/// path_action = "normalize"
704/// prompt_action = "wrap"
705///
706/// # Audit - Security event logging
707/// [audit]
708/// enabled = true
709/// file_path = "/var/log/kindly-guard/audit.log"
710/// max_file_size_mb = 100
711/// max_files = 10
712/// include_request_body = true
713/// include_response_body = false
714///
715/// # Shield Display
716/// [shield]
717/// enabled = true
718/// update_interval_ms = 1000
719/// detailed_stats = true
720/// color = true
721/// ```
722
723#[cfg(test)]
724mod tests {
725    use super::*;
726
727    #[test]
728    fn test_default_config() {
729        let config = Config::default();
730        assert!(config.server.stdio);
731        assert!(config.scanner.unicode_detection);
732        assert_eq!(config.server.port, 8080);
733    }
734
735    #[test]
736    fn test_security_validation() {
737        let mut config = Config::default();
738
739        // Should warn but not error with default config
740        assert!(config.validate_security().is_ok());
741
742        // Should error with weak JWT secret
743        config.auth.jwt_secret = Some("c2hvcnQ=".to_string()); // "short" in base64
744        assert!(config.validate_security().is_err());
745    }
746}