mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
execpolicy: unwrap PowerShell -Command wrappers on Windows (#20336)
## Why On Windows, Codex runs shell commands through a top-level `powershell.exe -NoProfile -Command ...` wrapper. `execpolicy` was matching that wrapper instead of the inner command, so prefix rules like `["git", "push"]` did not fire for PowerShell-wrapped commands even though the same normalization already happens for `bash -lc` on Unix. This change makes the Windows shell wrapper transparent to rule matching while preserving the existing Windows unmatched-command safelist and dangerous-command heuristics. ## What changed - add `parse_powershell_command_plain_commands()` in `shell-command/src/powershell.rs` to unwrap the top-level PowerShell `-Command` body with `extract_powershell_command()` and parse it with the existing PowerShell AST parser - update `core/src/exec_policy.rs` so `commands_for_exec_policy()` treats top-level PowerShell wrappers like `bash -lc` and evaluates rules against the parsed inner commands - carry a small `ExecPolicyCommandOrigin` through unmatched-command evaluation and expose `is_safe_powershell_words()` / `is_dangerous_powershell_words()` so Windows safelist and dangerous-command checks still work after unwrap - add Windows-focused tests for wrapped PowerShell prompt/allow matches, wrapper parsing, and unmatched safe/dangerous inner commands, and re-enable the end-to-end `execpolicy_blocks_shell_invocation` test on Windows ## Testing - `cargo test -p codex-shell-command`
This commit is contained in:
committed by
GitHub
Unverified
parent
0d9a5d20ec
commit
4f96001fa7
@@ -2,6 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
use crate::command_safety::try_parse_powershell_ast_commands;
|
||||
use crate::shell_detect::ShellType;
|
||||
use crate::shell_detect::detect_shell_type;
|
||||
|
||||
@@ -68,6 +69,18 @@ pub fn extract_powershell_command(command: &[String]) -> Option<(&str, &str)> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse the script body from a top-level PowerShell wrapper into argv-like commands.
|
||||
///
|
||||
/// This is intentionally narrower than the Windows safe-command parser: it only unwraps the
|
||||
/// `-Command`/`-c` body from a PowerShell invocation we already recognize, then delegates the
|
||||
/// script itself to the PowerShell AST parser.
|
||||
pub fn parse_powershell_command_into_plain_commands(
|
||||
command: &[String],
|
||||
) -> Option<Vec<Vec<String>>> {
|
||||
let (executable, script) = extract_powershell_command(command)?;
|
||||
try_parse_powershell_ast_commands(executable, script)
|
||||
}
|
||||
|
||||
/// This function attempts to find a powershell.exe executable on the system.
|
||||
pub fn try_find_powershell_executable_blocking() -> Option<AbsolutePathBuf> {
|
||||
try_find_powershellish_executable_in_path(&["powershell.exe"])
|
||||
@@ -139,6 +152,8 @@ fn is_powershellish_executable_available(powershell_or_pwsh_exe: &std::path::Pat
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::extract_powershell_command;
|
||||
#[cfg(windows)]
|
||||
use super::parse_powershell_command_into_plain_commands;
|
||||
|
||||
#[test]
|
||||
fn extracts_basic_powershell_command() {
|
||||
@@ -186,4 +201,38 @@ mod tests {
|
||||
let (_shell, script) = extract_powershell_command(&cmd).expect("extract");
|
||||
assert_eq!(script, "Get-ChildItem | Select-String foo");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn parses_plain_powershell_commands() {
|
||||
let commands = parse_powershell_command_into_plain_commands(&[
|
||||
"powershell.exe".to_string(),
|
||||
"-NoProfile".to_string(),
|
||||
"-Command".to_string(),
|
||||
"echo hi".to_string(),
|
||||
])
|
||||
.expect("parse");
|
||||
|
||||
assert_eq!(commands, vec![vec!["echo".to_string(), "hi".to_string()]]);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn parses_multiple_plain_powershell_commands() {
|
||||
let commands = parse_powershell_command_into_plain_commands(&[
|
||||
"powershell.exe".to_string(),
|
||||
"-NoProfile".to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output foo | Measure-Object".to_string(),
|
||||
])
|
||||
.expect("parse");
|
||||
|
||||
assert_eq!(
|
||||
commands,
|
||||
vec![
|
||||
vec!["Write-Output".to_string(), "foo".to_string()],
|
||||
vec!["Measure-Object".to_string()],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user