use std::path::PathBuf; use crate::shell::ShellType; use crate::shell::detect_shell_type; const POWERSHELL_FLAGS: &[&str] = &["-nologo", "-noprofile", "-command", "-c"]; /// Extract the PowerShell script body from an invocation such as: /// /// - ["pwsh", "-NoProfile", "-Command", "Get-ChildItem -Recurse | Select-String foo"] /// - ["powershell.exe", "-Command", "Write-Host hi"] /// - ["powershell", "-NoLogo", "-NoProfile", "-Command", "...script..."] /// /// Returns (`shell`, `script`) when the first arg is a PowerShell executable and a /// `-Command` (or `-c`) flag is present followed by a script string. pub fn extract_powershell_command(command: &[String]) -> Option<(&str, &str)> { if command.len() < 3 { return None; } let shell = &command[0]; if detect_shell_type(&PathBuf::from(shell)) != Some(ShellType::PowerShell) { return None; } // Find the first occurrence of -Command (accept common short alias -c as well) let mut i = 1usize; while i + 1 < command.len() { let flag = &command[i]; // Reject unknown flags if !POWERSHELL_FLAGS.contains(&flag.to_ascii_lowercase().as_str()) { return None; } if flag.eq_ignore_ascii_case("-Command") || flag.eq_ignore_ascii_case("-c") { let script = &command[i + 1]; return Some((shell, script.as_str())); } i += 1; } None } #[cfg(test)] mod tests { use super::extract_powershell_command; #[test] fn extracts_basic_powershell_command() { let cmd = vec![ "powershell".to_string(), "-Command".to_string(), "Write-Host hi".to_string(), ]; let (_shell, script) = extract_powershell_command(&cmd).expect("extract"); assert_eq!(script, "Write-Host hi"); } #[test] fn extracts_lowercase_flags() { let cmd = vec![ "powershell".to_string(), "-nologo".to_string(), "-command".to_string(), "Write-Host hi".to_string(), ]; let (_shell, script) = extract_powershell_command(&cmd).expect("extract"); assert_eq!(script, "Write-Host hi"); } #[test] fn extracts_full_path_powershell_command() { let command = if cfg!(windows) { "C:\\windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe".to_string() } else { "/usr/local/bin/powershell.exe".to_string() }; let cmd = vec![command, "-Command".to_string(), "Write-Host hi".to_string()]; let (_shell, script) = extract_powershell_command(&cmd).expect("extract"); assert_eq!(script, "Write-Host hi"); } #[test] fn extracts_with_noprofile_and_alias() { let cmd = vec![ "pwsh".to_string(), "-NoProfile".to_string(), "-c".to_string(), "Get-ChildItem | Select-String foo".to_string(), ]; let (_shell, script) = extract_powershell_command(&cmd).expect("extract"); assert_eq!(script, "Get-ChildItem | Select-String foo"); } }