rust_secure_logger/
lib.rs

1//! # Rust Secure Logger
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 hashing for tamper detection
10//! - **File Persistence**: Log rotation and disk persistence
11//! - **SIEM Integration**: CEF, LEEF, Syslog, and Splunk HEC formats
12//! - **Compliance Reporting**: SOX, GLBA, PCI-DSS automated reports
13//! - **Audit Trail**: Immutable log entries with timestamps
14//!
15//! ## Alignment with Federal Guidance
16//!
17//! This library aligns with 2024 CISA/FBI guidance recommending memory-safe
18//! languages for critical infrastructure to eliminate 70% of security vulnerabilities.
19//!
20//! ## Quick Start
21//!
22//! ```rust
23//! use rust_secure_logger::SecureLogger;
24//!
25//! let logger = SecureLogger::new();
26//! logger.info("Application started");
27//! logger.audit("User authentication successful", Some(serde_json::json!({
28//!     "user_id": "12345",
29//!     "ip_address": "192.168.1.100"
30//! })));
31//! ```
32
33pub mod entry;
34pub mod persistence;
35pub mod formats;
36pub mod compliance;
37
38pub use entry::{LogEntry, SecurityLevel};
39pub use persistence::{LogWriter, PersistenceConfig};
40pub use formats::{CEFFormatter, LEEFFormatter, SyslogFormatter, SplunkFormatter};
41pub use compliance::{ComplianceFramework, ComplianceReport, ComplianceReporter};
42
43use std::sync::{Arc, Mutex};
44
45/// Thread-safe secure logger for financial systems
46#[derive(Clone)]
47pub struct SecureLogger {
48    entries: Arc<Mutex<Vec<LogEntry>>>,
49    source: Option<String>,
50}
51
52impl SecureLogger {
53    /// Create a new secure logger instance
54    pub fn new() -> Self {
55        Self {
56            entries: Arc::new(Mutex::new(Vec::new())),
57            source: None,
58        }
59    }
60
61    /// Create a logger with a source identifier (hostname, service name)
62    pub fn with_source(source: impl Into<String>) -> Self {
63        Self {
64            entries: Arc::new(Mutex::new(Vec::new())),
65            source: Some(source.into()),
66        }
67    }
68
69    /// Log an informational message
70    pub fn info(&self, message: impl Into<String>) {
71        self.log(SecurityLevel::Info, message.into(), None, None);
72    }
73
74    /// Log a warning
75    pub fn warning(&self, message: impl Into<String>) {
76        self.log(SecurityLevel::Warning, message.into(), None, None);
77    }
78
79    /// Log a security event with optional metadata
80    pub fn security_event(&self, message: impl Into<String>, metadata: Option<serde_json::Value>) {
81        self.log(SecurityLevel::SecurityEvent, message.into(), metadata, None);
82    }
83
84    /// Log a critical security incident
85    pub fn critical(&self, message: impl Into<String>, metadata: Option<serde_json::Value>) {
86        self.log(SecurityLevel::Critical, message.into(), metadata, None);
87    }
88
89    /// Log an audit trail entry (for financial transactions, access control, etc.)
90    pub fn audit(&self, message: impl Into<String>, metadata: Option<serde_json::Value>) {
91        self.log(SecurityLevel::Audit, message.into(), metadata, None);
92    }
93
94    /// Log with category
95    pub fn log_with_category(
96        &self,
97        level: SecurityLevel,
98        message: impl Into<String>,
99        metadata: Option<serde_json::Value>,
100        category: impl Into<String>,
101    ) {
102        self.log(level, message.into(), metadata, Some(category.into()));
103    }
104
105    /// Internal logging function
106    fn log(&self, level: SecurityLevel, message: String, metadata: Option<serde_json::Value>, category: Option<String>) {
107        let entry = LogEntry::new_with_context(level, message, metadata, self.source.clone(), category);
108        let mut entries = self.entries.lock().unwrap();
109        entries.push(entry);
110    }
111
112    /// Get all log entries (read-only)
113    pub fn get_entries(&self) -> Vec<LogEntry> {
114        let entries = self.entries.lock().unwrap();
115        entries.clone()
116    }
117
118    /// Get entries filtered by security level
119    pub fn get_entries_by_level(&self, level: SecurityLevel) -> Vec<LogEntry> {
120        let entries = self.entries.lock().unwrap();
121        entries
122            .iter()
123            .filter(|e| e.level == level)
124            .cloned()
125            .collect()
126    }
127
128    /// Get entries by category
129    pub fn get_entries_by_category(&self, category: &str) -> Vec<LogEntry> {
130        let entries = self.entries.lock().unwrap();
131        entries
132            .iter()
133            .filter(|e| e.category.as_deref() == Some(category))
134            .cloned()
135            .collect()
136    }
137
138    /// Verify integrity of all log entries
139    pub fn verify_all_integrity(&self) -> bool {
140        let entries = self.entries.lock().unwrap();
141        entries.iter().all(|entry| entry.verify_integrity())
142    }
143
144    /// Export logs as JSON
145    pub fn export_json(&self) -> Result<String, serde_json::Error> {
146        let entries = self.entries.lock().unwrap();
147        serde_json::to_string_pretty(&*entries)
148    }
149
150    /// Export logs in CEF format (for ArcSight)
151    pub fn export_cef(&self) -> Vec<String> {
152        let entries = self.entries.lock().unwrap();
153        entries.iter().map(|e| CEFFormatter::format(e)).collect()
154    }
155
156    /// Export logs in LEEF format (for QRadar)
157    pub fn export_leef(&self) -> Vec<String> {
158        let entries = self.entries.lock().unwrap();
159        entries.iter().map(|e| LEEFFormatter::format(e)).collect()
160    }
161
162    /// Export logs in Syslog format
163    pub fn export_syslog(&self) -> Vec<String> {
164        let entries = self.entries.lock().unwrap();
165        entries.iter().map(|e| SyslogFormatter::format(e)).collect()
166    }
167
168    /// Export logs in Splunk HEC format
169    pub fn export_splunk(&self) -> Vec<String> {
170        let entries = self.entries.lock().unwrap();
171        entries.iter().map(|e| SplunkFormatter::format(e)).collect()
172    }
173
174    /// Get count of entries by security level
175    pub fn count_by_level(&self, level: SecurityLevel) -> usize {
176        let entries = self.entries.lock().unwrap();
177        entries.iter().filter(|e| e.level == level).count()
178    }
179
180    /// Get entries within a date range
181    pub fn get_entries_by_date_range(
182        &self,
183        start: chrono::DateTime<chrono::Utc>,
184        end: chrono::DateTime<chrono::Utc>,
185    ) -> Vec<LogEntry> {
186        let entries = self.entries.lock().unwrap();
187        entries
188            .iter()
189            .filter(|e| e.timestamp >= start && e.timestamp <= end)
190            .cloned()
191            .collect()
192    }
193
194    /// Clear all log entries (use with caution - breaks audit trail)
195    pub fn clear(&self) {
196        let mut entries = self.entries.lock().unwrap();
197        entries.clear()
198    }
199
200    /// Get the most recent N entries
201    pub fn get_recent_entries(&self, count: usize) -> Vec<LogEntry> {
202        let entries = self.entries.lock().unwrap();
203        let total = entries.len();
204        if total <= count {
205            entries.clone()
206        } else {
207            entries[total - count..].to_vec()
208        }
209    }
210
211    /// Search log entries by message content
212    pub fn search(&self, query: &str) -> Vec<LogEntry> {
213        let entries = self.entries.lock().unwrap();
214        entries
215            .iter()
216            .filter(|e| e.message.contains(query))
217            .cloned()
218            .collect()
219    }
220
221    /// Get statistics about log entries
222    pub fn get_statistics(&self) -> LogStatistics {
223        let entries = self.entries.lock().unwrap();
224        let total_entries = entries.len();
225
226        let mut stats = LogStatistics {
227            total_entries,
228            info_count: 0,
229            warning_count: 0,
230            security_event_count: 0,
231            critical_count: 0,
232            audit_count: 0,
233        };
234
235        for entry in entries.iter() {
236            match entry.level {
237                SecurityLevel::Info => stats.info_count += 1,
238                SecurityLevel::Warning => stats.warning_count += 1,
239                SecurityLevel::SecurityEvent => stats.security_event_count += 1,
240                SecurityLevel::Critical => stats.critical_count += 1,
241                SecurityLevel::Audit => stats.audit_count += 1,
242            }
243        }
244
245        stats
246    }
247
248    /// Generate SOX compliance report
249    pub fn generate_sox_report(
250        &self,
251        period_start: chrono::DateTime<chrono::Utc>,
252        period_end: chrono::DateTime<chrono::Utc>,
253    ) -> ComplianceReport {
254        let entries = self.get_entries();
255        ComplianceReporter::generate_sox_report(&entries, period_start, period_end)
256    }
257
258    /// Generate PCI-DSS compliance report
259    pub fn generate_pci_report(
260        &self,
261        period_start: chrono::DateTime<chrono::Utc>,
262        period_end: chrono::DateTime<chrono::Utc>,
263    ) -> ComplianceReport {
264        let entries = self.get_entries();
265        ComplianceReporter::generate_pci_report(&entries, period_start, period_end)
266    }
267
268    /// Generate GLBA compliance report
269    pub fn generate_glba_report(
270        &self,
271        period_start: chrono::DateTime<chrono::Utc>,
272        period_end: chrono::DateTime<chrono::Utc>,
273    ) -> ComplianceReport {
274        let entries = self.get_entries();
275        ComplianceReporter::generate_glba_report(&entries, period_start, period_end)
276    }
277}
278
279/// Statistics about log entries
280#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
281pub struct LogStatistics {
282    pub total_entries: usize,
283    pub info_count: usize,
284    pub warning_count: usize,
285    pub security_event_count: usize,
286    pub critical_count: usize,
287    pub audit_count: usize,
288}
289
290impl Default for SecureLogger {
291    fn default() -> Self {
292        Self::new()
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn test_secure_logger() {
302        let logger = SecureLogger::new();
303        logger.info("Application started");
304        logger.warning("High memory usage detected");
305        logger.audit("User authentication successful", Some(serde_json::json!({
306            "user_id": "12345",
307            "timestamp": "2024-11-06T00:00:00Z"
308        })));
309
310        assert_eq!(logger.get_entries().len(), 3);
311        assert_eq!(logger.count_by_level(SecurityLevel::Info), 1);
312        assert_eq!(logger.count_by_level(SecurityLevel::Warning), 1);
313        assert_eq!(logger.count_by_level(SecurityLevel::Audit), 1);
314    }
315
316    #[test]
317    fn test_thread_safety() {
318        use std::thread;
319
320        let logger = SecureLogger::new();
321        let mut handles = vec![];
322
323        for i in 0..10 {
324            let logger_clone = logger.clone();
325            let handle = thread::spawn(move || {
326                logger_clone.info(format!("Thread {} message", i));
327            });
328            handles.push(handle);
329        }
330
331        for handle in handles {
332            handle.join().unwrap();
333        }
334
335        assert_eq!(logger.get_entries().len(), 10);
336    }
337
338    #[test]
339    fn test_cef_export() {
340        let logger = SecureLogger::new();
341        logger.security_event("Failed login attempt", Some(serde_json::json!({
342            "username": "admin"
343        })));
344
345        let cef_logs = logger.export_cef();
346        assert_eq!(cef_logs.len(), 1);
347        assert!(cef_logs[0].starts_with("CEF:0|"));
348    }
349
350    #[test]
351    fn test_leef_export() {
352        let logger = SecureLogger::new();
353        logger.critical("Security breach detected", None);
354
355        let leef_logs = logger.export_leef();
356        assert_eq!(leef_logs.len(), 1);
357        assert!(leef_logs[0].starts_with("LEEF:2.0|"));
358    }
359
360    #[test]
361    fn test_compliance_reporting() {
362        let logger = SecureLogger::new();
363        logger.audit("Transaction processed", Some(serde_json::json!({
364            "amount": 1000,
365            "currency": "USD"
366        })));
367
368        let start = chrono::Utc::now() - chrono::Duration::hours(1);
369        let end = chrono::Utc::now();
370
371        let sox_report = logger.generate_sox_report(start, end);
372        assert_eq!(sox_report.framework, ComplianceFramework::SOX);
373        assert_eq!(sox_report.audit_events, 1);
374    }
375
376    #[test]
377    fn test_logger_with_source() {
378        let logger = SecureLogger::with_source("web-server-01");
379        logger.info("Server started");
380
381        let entries = logger.get_entries();
382        assert_eq!(entries[0].source, Some("web-server-01".to_string()));
383    }
384
385    #[test]
386    fn test_category_filtering() {
387        let logger = SecureLogger::new();
388        logger.log_with_category(
389            SecurityLevel::SecurityEvent,
390            "Login failed",
391            None,
392            "authentication"
393        );
394        logger.log_with_category(
395            SecurityLevel::Info,
396            "Page loaded",
397            None,
398            "web"
399        );
400
401        let auth_events = logger.get_entries_by_category("authentication");
402        assert_eq!(auth_events.len(), 1);
403        assert_eq!(auth_events[0].message, "Login failed");
404    }
405}