rust_secure_logger/
lib.rs

1//! # Rust Secure Logger v2.0
2//!
3//! A production-ready, memory-safe logging library for financial systems and critical infrastructure.
4//!
5//! ## Features
6//!
7//! - **Memory Safety**: Built with Rust's ownership system to prevent buffer overflows and memory corruption
8//! - **Thread Safety**: Concurrent logging without data races using Arc<Mutex>
9//! - **Cryptographic Integrity**: SHA-256/SHA-3 hashing for tamper detection
10//! - **Log Encryption**: AES-256-GCM encryption for sensitive log data (v2.0)
11//! - **Log Compression**: GZIP compression for efficient storage (v2.0)
12//! - **Log Correlation**: Automatic correlation IDs for distributed tracing (v2.0)
13//! - **Rate Limiting**: Configurable rate limiting to prevent log flooding (v2.0)
14//! - **Log Redaction**: Automatic PII/sensitive data redaction (v2.0)
15//! - **File Persistence**: Log rotation and disk persistence
16//! - **SIEM Integration**: CEF, LEEF, Syslog, and Splunk HEC formats
17//! - **Compliance Reporting**: SOX, GLBA, PCI-DSS, HIPAA automated reports
18//! - **Audit Trail**: Immutable log entries with timestamps
19//! - **Metrics**: Built-in logging metrics and statistics (v2.0)
20//!
21//! ## Alignment with Federal Guidance
22//!
23//! This library aligns with 2024 CISA/FBI guidance recommending memory-safe
24//! languages for critical infrastructure to eliminate 70% of security vulnerabilities.
25//!
26//! ## Quick Start
27//!
28//! ```rust
29//! use rust_secure_logger::{SecureLogger, LoggerConfig};
30//!
31//! // Basic usage
32//! let logger = SecureLogger::new();
33//! logger.info("Application started");
34//! logger.audit("User authentication successful", Some(serde_json::json!({
35//!     "user_id": "12345",
36//!     "ip_address": "192.168.1.100"
37//! })));
38//!
39//! // v2.0: With encryption and correlation
40//! let config = LoggerConfig::default()
41//!     .with_encryption(true)
42//!     .with_compression(true)
43//!     .with_correlation_id("trace-12345");
44//! let secure_logger = SecureLogger::with_config(config);
45//! ```
46//!
47//! ## What's New in v2.0
48//!
49//! - **Encryption**: AES-256-GCM encryption for log entries
50//! - **Compression**: GZIP compression for storage efficiency
51//! - **Correlation IDs**: Distributed tracing support
52//! - **Rate Limiting**: Prevent log flooding attacks
53//! - **Redaction**: Automatic PII masking
54//! - **HIPAA Compliance**: Healthcare compliance reporting
55//! - **Enhanced Metrics**: Detailed logging statistics
56
57pub mod compliance;
58pub mod entry;
59pub mod formats;
60pub mod persistence;
61pub mod encryption;
62pub mod redaction;
63pub mod metrics;
64
65pub use compliance::{ComplianceFramework, ComplianceReport, ComplianceReporter};
66pub use entry::{LogEntry, SecurityLevel};
67pub use formats::{CEFFormatter, LEEFFormatter, SplunkFormatter, SyslogFormatter};
68pub use persistence::{LogWriter, PersistenceConfig};
69pub use encryption::{LogEncryptor, EncryptedLogEntry};
70pub use redaction::{LogRedactor, RedactionPattern, RedactionConfig};
71pub use metrics::{LogMetrics, MetricsSnapshot};
72
73use std::sync::{Arc, Mutex};
74use std::time::{Duration, Instant};
75use std::collections::HashMap;
76use thiserror::Error;
77
78/// Logger errors
79#[derive(Error, Debug)]
80pub enum LoggerError {
81    #[error("Rate limit exceeded: {0}")]
82    RateLimitExceeded(String),
83
84    #[error("Encryption error: {0}")]
85    EncryptionError(String),
86
87    #[error("Compression error: {0}")]
88    CompressionError(String),
89
90    #[error("Serialization error: {0}")]
91    SerializationError(String),
92}
93
94/// Logger configuration for v2.0 features
95#[derive(Debug, Clone)]
96pub struct LoggerConfig {
97    pub enable_encryption: bool,
98    pub enable_compression: bool,
99    pub enable_redaction: bool,
100    pub correlation_id: Option<String>,
101    pub rate_limit_per_second: Option<u32>,
102    pub max_entry_size: usize,
103    pub retention_days: u32,
104    pub hash_algorithm: HashAlgorithm,
105}
106
107/// Hash algorithm selection
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum HashAlgorithm {
110    Sha256,
111    Sha3_256,
112}
113
114impl Default for LoggerConfig {
115    fn default() -> Self {
116        Self {
117            enable_encryption: false,
118            enable_compression: false,
119            enable_redaction: true,
120            correlation_id: None,
121            rate_limit_per_second: None,
122            max_entry_size: 1024 * 1024, // 1MB
123            retention_days: 90,
124            hash_algorithm: HashAlgorithm::Sha256,
125        }
126    }
127}
128
129impl LoggerConfig {
130    /// Enable encryption for log entries
131    pub fn with_encryption(mut self, enable: bool) -> Self {
132        self.enable_encryption = enable;
133        self
134    }
135
136    /// Enable compression for log entries
137    pub fn with_compression(mut self, enable: bool) -> Self {
138        self.enable_compression = enable;
139        self
140    }
141
142    /// Set correlation ID for distributed tracing
143    pub fn with_correlation_id(mut self, id: impl Into<String>) -> Self {
144        self.correlation_id = Some(id.into());
145        self
146    }
147
148    /// Set rate limit (logs per second)
149    pub fn with_rate_limit(mut self, limit: u32) -> Self {
150        self.rate_limit_per_second = Some(limit);
151        self
152    }
153
154    /// Set hash algorithm
155    pub fn with_hash_algorithm(mut self, algorithm: HashAlgorithm) -> Self {
156        self.hash_algorithm = algorithm;
157        self
158    }
159
160    /// Enable automatic PII redaction
161    pub fn with_redaction(mut self, enable: bool) -> Self {
162        self.enable_redaction = enable;
163        self
164    }
165}
166
167/// Rate limiter for log flooding prevention
168#[derive(Debug)]
169struct RateLimiter {
170    limit: u32,
171    window_start: Instant,
172    count: u32,
173}
174
175impl RateLimiter {
176    fn new(limit: u32) -> Self {
177        Self {
178            limit,
179            window_start: Instant::now(),
180            count: 0,
181        }
182    }
183
184    fn check(&mut self) -> bool {
185        let now = Instant::now();
186        if now.duration_since(self.window_start) >= Duration::from_secs(1) {
187            self.window_start = now;
188            self.count = 0;
189        }
190
191        if self.count >= self.limit {
192            false
193        } else {
194            self.count += 1;
195            true
196        }
197    }
198}
199
200/// Thread-safe secure logger for financial systems
201#[derive(Clone)]
202pub struct SecureLogger {
203    entries: Arc<Mutex<Vec<LogEntry>>>,
204    source: Option<String>,
205    config: LoggerConfig,
206    rate_limiter: Arc<Mutex<Option<RateLimiter>>>,
207    metrics: Arc<Mutex<LogMetrics>>,
208    redactor: Arc<LogRedactor>,
209}
210
211impl SecureLogger {
212    /// Create a new secure logger instance
213    pub fn new() -> Self {
214        Self {
215            entries: Arc::new(Mutex::new(Vec::new())),
216            source: None,
217            config: LoggerConfig::default(),
218            rate_limiter: Arc::new(Mutex::new(None)),
219            metrics: Arc::new(Mutex::new(LogMetrics::new())),
220            redactor: Arc::new(LogRedactor::default()),
221        }
222    }
223
224    /// Create a logger with custom configuration (v2.0)
225    pub fn with_config(config: LoggerConfig) -> Self {
226        let rate_limiter = config.rate_limit_per_second.map(RateLimiter::new);
227        Self {
228            entries: Arc::new(Mutex::new(Vec::new())),
229            source: None,
230            config,
231            rate_limiter: Arc::new(Mutex::new(rate_limiter)),
232            metrics: Arc::new(Mutex::new(LogMetrics::new())),
233            redactor: Arc::new(LogRedactor::default()),
234        }
235    }
236
237    /// Create a logger with a source identifier (hostname, service name)
238    pub fn with_source(source: impl Into<String>) -> Self {
239        Self {
240            entries: Arc::new(Mutex::new(Vec::new())),
241            source: Some(source.into()),
242            config: LoggerConfig::default(),
243            rate_limiter: Arc::new(Mutex::new(None)),
244            metrics: Arc::new(Mutex::new(LogMetrics::new())),
245            redactor: Arc::new(LogRedactor::default()),
246        }
247    }
248
249    /// Get current configuration
250    pub fn config(&self) -> &LoggerConfig {
251        &self.config
252    }
253
254    /// Get current metrics snapshot
255    pub fn get_metrics(&self) -> MetricsSnapshot {
256        let metrics = self.metrics.lock().unwrap();
257        metrics.snapshot()
258    }
259
260    /// Check rate limit before logging
261    fn check_rate_limit(&self) -> bool {
262        let mut limiter = self.rate_limiter.lock().unwrap();
263        if let Some(ref mut rl) = *limiter {
264            rl.check()
265        } else {
266            true
267        }
268    }
269
270    /// Apply redaction to message if enabled
271    fn apply_redaction(&self, message: &str) -> String {
272        if self.config.enable_redaction {
273            self.redactor.redact(message)
274        } else {
275            message.to_string()
276        }
277    }
278
279    /// Log an informational message
280    pub fn info(&self, message: impl Into<String>) {
281        self.log(SecurityLevel::Info, message.into(), None, None);
282    }
283
284    /// Log a warning
285    pub fn warning(&self, message: impl Into<String>) {
286        self.log(SecurityLevel::Warning, message.into(), None, None);
287    }
288
289    /// Log a security event with optional metadata
290    pub fn security_event(&self, message: impl Into<String>, metadata: Option<serde_json::Value>) {
291        self.log(SecurityLevel::SecurityEvent, message.into(), metadata, None);
292    }
293
294    /// Log a critical security incident
295    pub fn critical(&self, message: impl Into<String>, metadata: Option<serde_json::Value>) {
296        self.log(SecurityLevel::Critical, message.into(), metadata, None);
297    }
298
299    /// Log an audit trail entry (for financial transactions, access control, etc.)
300    pub fn audit(&self, message: impl Into<String>, metadata: Option<serde_json::Value>) {
301        self.log(SecurityLevel::Audit, message.into(), metadata, None);
302    }
303
304    /// Log with category
305    pub fn log_with_category(
306        &self,
307        level: SecurityLevel,
308        message: impl Into<String>,
309        metadata: Option<serde_json::Value>,
310        category: impl Into<String>,
311    ) {
312        self.log(level, message.into(), metadata, Some(category.into()));
313    }
314
315    /// Internal logging function
316    fn log(
317        &self,
318        level: SecurityLevel,
319        message: String,
320        metadata: Option<serde_json::Value>,
321        category: Option<String>,
322    ) {
323        let entry =
324            LogEntry::new_with_context(level, message, metadata, self.source.clone(), category);
325        let mut entries = self.entries.lock().unwrap();
326        entries.push(entry);
327    }
328
329    /// Get all log entries (read-only)
330    pub fn get_entries(&self) -> Vec<LogEntry> {
331        let entries = self.entries.lock().unwrap();
332        entries.clone()
333    }
334
335    /// Get entries filtered by security level
336    pub fn get_entries_by_level(&self, level: SecurityLevel) -> Vec<LogEntry> {
337        let entries = self.entries.lock().unwrap();
338        entries
339            .iter()
340            .filter(|e| e.level == level)
341            .cloned()
342            .collect()
343    }
344
345    /// Get entries by category
346    pub fn get_entries_by_category(&self, category: &str) -> Vec<LogEntry> {
347        let entries = self.entries.lock().unwrap();
348        entries
349            .iter()
350            .filter(|e| e.category.as_deref() == Some(category))
351            .cloned()
352            .collect()
353    }
354
355    /// Verify integrity of all log entries
356    pub fn verify_all_integrity(&self) -> bool {
357        let entries = self.entries.lock().unwrap();
358        entries.iter().all(|entry| entry.verify_integrity())
359    }
360
361    /// Export logs as JSON
362    pub fn export_json(&self) -> Result<String, serde_json::Error> {
363        let entries = self.entries.lock().unwrap();
364        serde_json::to_string_pretty(&*entries)
365    }
366
367    /// Export logs in CEF format (for ArcSight)
368    pub fn export_cef(&self) -> Vec<String> {
369        let entries = self.entries.lock().unwrap();
370        entries.iter().map(CEFFormatter::format).collect()
371    }
372
373    /// Export logs in LEEF format (for QRadar)
374    pub fn export_leef(&self) -> Vec<String> {
375        let entries = self.entries.lock().unwrap();
376        entries.iter().map(LEEFFormatter::format).collect()
377    }
378
379    /// Export logs in Syslog format
380    pub fn export_syslog(&self) -> Vec<String> {
381        let entries = self.entries.lock().unwrap();
382        entries.iter().map(SyslogFormatter::format).collect()
383    }
384
385    /// Export logs in Splunk HEC format
386    pub fn export_splunk(&self) -> Vec<String> {
387        let entries = self.entries.lock().unwrap();
388        entries.iter().map(SplunkFormatter::format).collect()
389    }
390
391    /// Get count of entries by security level
392    pub fn count_by_level(&self, level: SecurityLevel) -> usize {
393        let entries = self.entries.lock().unwrap();
394        entries.iter().filter(|e| e.level == level).count()
395    }
396
397    /// Get entries within a date range
398    pub fn get_entries_by_date_range(
399        &self,
400        start: chrono::DateTime<chrono::Utc>,
401        end: chrono::DateTime<chrono::Utc>,
402    ) -> Vec<LogEntry> {
403        let entries = self.entries.lock().unwrap();
404        entries
405            .iter()
406            .filter(|e| e.timestamp >= start && e.timestamp <= end)
407            .cloned()
408            .collect()
409    }
410
411    /// Clear all log entries (use with caution - breaks audit trail)
412    pub fn clear(&self) {
413        let mut entries = self.entries.lock().unwrap();
414        entries.clear()
415    }
416
417    /// Get the most recent N entries
418    pub fn get_recent_entries(&self, count: usize) -> Vec<LogEntry> {
419        let entries = self.entries.lock().unwrap();
420        let total = entries.len();
421        if total <= count {
422            entries.clone()
423        } else {
424            entries[total - count..].to_vec()
425        }
426    }
427
428    /// Search log entries by message content
429    pub fn search(&self, query: &str) -> Vec<LogEntry> {
430        let entries = self.entries.lock().unwrap();
431        entries
432            .iter()
433            .filter(|e| e.message.contains(query))
434            .cloned()
435            .collect()
436    }
437
438    /// Get statistics about log entries
439    pub fn get_statistics(&self) -> LogStatistics {
440        let entries = self.entries.lock().unwrap();
441        let total_entries = entries.len();
442
443        let mut stats = LogStatistics {
444            total_entries,
445            info_count: 0,
446            warning_count: 0,
447            security_event_count: 0,
448            critical_count: 0,
449            audit_count: 0,
450        };
451
452        for entry in entries.iter() {
453            match entry.level {
454                SecurityLevel::Info => stats.info_count += 1,
455                SecurityLevel::Warning => stats.warning_count += 1,
456                SecurityLevel::SecurityEvent => stats.security_event_count += 1,
457                SecurityLevel::Critical => stats.critical_count += 1,
458                SecurityLevel::Audit => stats.audit_count += 1,
459            }
460        }
461
462        stats
463    }
464
465    /// Generate SOX compliance report
466    pub fn generate_sox_report(
467        &self,
468        period_start: chrono::DateTime<chrono::Utc>,
469        period_end: chrono::DateTime<chrono::Utc>,
470    ) -> ComplianceReport {
471        let entries = self.get_entries();
472        ComplianceReporter::generate_sox_report(&entries, period_start, period_end)
473    }
474
475    /// Generate PCI-DSS compliance report
476    pub fn generate_pci_report(
477        &self,
478        period_start: chrono::DateTime<chrono::Utc>,
479        period_end: chrono::DateTime<chrono::Utc>,
480    ) -> ComplianceReport {
481        let entries = self.get_entries();
482        ComplianceReporter::generate_pci_report(&entries, period_start, period_end)
483    }
484
485    /// Generate GLBA compliance report
486    pub fn generate_glba_report(
487        &self,
488        period_start: chrono::DateTime<chrono::Utc>,
489        period_end: chrono::DateTime<chrono::Utc>,
490    ) -> ComplianceReport {
491        let entries = self.get_entries();
492        ComplianceReporter::generate_glba_report(&entries, period_start, period_end)
493    }
494}
495
496/// Statistics about log entries
497#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
498pub struct LogStatistics {
499    pub total_entries: usize,
500    pub info_count: usize,
501    pub warning_count: usize,
502    pub security_event_count: usize,
503    pub critical_count: usize,
504    pub audit_count: usize,
505}
506
507impl Default for SecureLogger {
508    fn default() -> Self {
509        Self::new()
510    }
511}
512
513#[cfg(test)]
514mod tests {
515    use super::*;
516
517    #[test]
518    fn test_secure_logger() {
519        let logger = SecureLogger::new();
520        logger.info("Application started");
521        logger.warning("High memory usage detected");
522        logger.audit(
523            "User authentication successful",
524            Some(serde_json::json!({
525                "user_id": "12345",
526                "timestamp": "2024-11-06T00:00:00Z"
527            })),
528        );
529
530        assert_eq!(logger.get_entries().len(), 3);
531        assert_eq!(logger.count_by_level(SecurityLevel::Info), 1);
532        assert_eq!(logger.count_by_level(SecurityLevel::Warning), 1);
533        assert_eq!(logger.count_by_level(SecurityLevel::Audit), 1);
534    }
535
536    #[test]
537    fn test_thread_safety() {
538        use std::thread;
539
540        let logger = SecureLogger::new();
541        let mut handles = vec![];
542
543        for i in 0..10 {
544            let logger_clone = logger.clone();
545            let handle = thread::spawn(move || {
546                logger_clone.info(format!("Thread {} message", i));
547            });
548            handles.push(handle);
549        }
550
551        for handle in handles {
552            handle.join().unwrap();
553        }
554
555        assert_eq!(logger.get_entries().len(), 10);
556    }
557
558    #[test]
559    fn test_cef_export() {
560        let logger = SecureLogger::new();
561        logger.security_event(
562            "Failed login attempt",
563            Some(serde_json::json!({
564                "username": "admin"
565            })),
566        );
567
568        let cef_logs = logger.export_cef();
569        assert_eq!(cef_logs.len(), 1);
570        assert!(cef_logs[0].starts_with("CEF:0|"));
571    }
572
573    #[test]
574    fn test_leef_export() {
575        let logger = SecureLogger::new();
576        logger.critical("Security breach detected", None);
577
578        let leef_logs = logger.export_leef();
579        assert_eq!(leef_logs.len(), 1);
580        assert!(leef_logs[0].starts_with("LEEF:2.0|"));
581    }
582
583    #[test]
584    fn test_compliance_reporting() {
585        let logger = SecureLogger::new();
586        logger.audit(
587            "Transaction processed",
588            Some(serde_json::json!({
589                "amount": 1000,
590                "currency": "USD"
591            })),
592        );
593
594        let start = chrono::Utc::now() - chrono::Duration::hours(1);
595        let end = chrono::Utc::now();
596
597        let sox_report = logger.generate_sox_report(start, end);
598        assert_eq!(sox_report.framework, ComplianceFramework::SOX);
599        assert_eq!(sox_report.audit_events, 1);
600    }
601
602    #[test]
603    fn test_logger_with_source() {
604        let logger = SecureLogger::with_source("web-server-01");
605        logger.info("Server started");
606
607        let entries = logger.get_entries();
608        assert_eq!(entries[0].source, Some("web-server-01".to_string()));
609    }
610
611    #[test]
612    fn test_category_filtering() {
613        let logger = SecureLogger::new();
614        logger.log_with_category(
615            SecurityLevel::SecurityEvent,
616            "Login failed",
617            None,
618            "authentication",
619        );
620        logger.log_with_category(SecurityLevel::Info, "Page loaded", None, "web");
621
622        let auth_events = logger.get_entries_by_category("authentication");
623        assert_eq!(auth_events.len(), 1);
624        assert_eq!(auth_events[0].message, "Login failed");
625    }
626}