Skip to main content

par_term/tab/
session_logging.rs

1//! Tab session logging methods.
2//!
3//! Provides methods for toggling session logging on/off and querying its state.
4
5use crate::config::Config;
6use crate::session_logger::SessionLogger;
7use crate::tab::Tab;
8use std::sync::Arc;
9
10impl Tab {
11    /// Toggle session logging on/off.
12    ///
13    /// Returns `Ok(true)` if logging is now active, `Ok(false)` if stopped.
14    /// If logging wasn't active and no logger exists, creates a new one.
15    pub fn toggle_session_logging(&mut self, config: &Config) -> anyhow::Result<bool> {
16        let mut logger_guard = self.session_logger.lock();
17
18        if let Some(ref mut logger) = *logger_guard {
19            // Logger exists - toggle based on current state
20            if logger.is_active() {
21                logger.stop()?;
22                log::info!("Session logging stopped via hotkey");
23                Ok(false)
24            } else {
25                logger.start()?;
26                log::info!("Session logging started via hotkey");
27                Ok(true)
28            }
29        } else {
30            // No logger exists - create one and start it
31            let logs_dir = config.logs_dir();
32            if let Err(e) = std::fs::create_dir_all(&logs_dir) {
33                log::warn!("Failed to create logs directory: {}", e);
34                return Err(anyhow::anyhow!("Failed to create logs directory: {}", e));
35            }
36            // SEC-010: Enforce restrictive directory permissions so that session logs
37            // (which may capture terminal output including passwords) are not
38            // world-listable even if the user's umask is permissive.
39            #[cfg(unix)]
40            {
41                use std::os::unix::fs::PermissionsExt;
42                let _ = std::fs::set_permissions(&logs_dir, std::fs::Permissions::from_mode(0o700));
43            }
44
45            // Get terminal dimensions
46            let dimensions = if let Ok(term) = self.terminal.try_read() {
47                term.dimensions()
48            } else {
49                (80, 24) // fallback
50            };
51
52            let session_title = Some(format!(
53                "{} - {}",
54                self.title,
55                chrono::Local::now().format("%Y-%m-%d %H:%M:%S")
56            ));
57
58            let mut logger = SessionLogger::new(
59                config.session_log.session_log_format,
60                &logs_dir,
61                dimensions,
62                session_title,
63            )?;
64
65            logger.set_redact_passwords(config.session_log.session_log_redact_passwords);
66            logger.start()?;
67
68            // SEC-002: Emit a prominent one-time warning when session logging is enabled.
69            // Session logs capture raw terminal I/O and may include passwords, API keys,
70            // tokens, and other credentials even with heuristic redaction active.
71            eprintln!(
72                "\n[par-term SESSION LOGGING ENABLED]\n\
73                 Session output is now being recorded to: {}\n\
74                 WARNING: Session logs may capture sensitive data (passwords, API keys, tokens)\n\
75                 despite heuristic redaction. Password-prompt detection is enabled: {}\n\
76                 Do NOT share session log files unless you have reviewed their contents.\n\
77                 Disable session logging when working with sensitive credentials.\n",
78                logs_dir.display(),
79                config.session_log.session_log_redact_passwords,
80            );
81
82            // Set up output callback to record PTY output
83            let logger_clone = Arc::clone(&self.session_logger);
84            if let Ok(term) = self.terminal.try_read() {
85                term.set_output_callback(move |data: &[u8]| {
86                    if let Some(ref mut logger) = *logger_clone.lock() {
87                        logger.record_output(data);
88                    }
89                });
90            }
91
92            *logger_guard = Some(logger);
93            log::info!("Session logging created and started via hotkey");
94            Ok(true)
95        }
96    }
97
98    /// Check if session logging is currently active.
99    pub fn is_session_logging_active(&self) -> bool {
100        if let Some(ref logger) = *self.session_logger.lock() {
101            logger.is_active()
102        } else {
103            false
104        }
105    }
106}