mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Use in-process parsing for PowerShell command classification (#39602)
## What changed - Route PowerShell wrapper extraction and Windows safe-command classification through the tree-sitter lowerer. - Compile the PowerShell AST subprocess parser only in tests, where it remains available as an oracle. GitOrigin-RevId: 3690bc32e8987ac9de820ef39ff7401d98ea8d83
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
// Keep the PowerShell subprocess parser available as a test oracle, but do not
|
||||
// compile it into production command classification.
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod powershell_parser;
|
||||
mod powershell_tree_sitter;
|
||||
|
||||
@@ -5,5 +9,4 @@ pub mod is_dangerous_command;
|
||||
pub mod is_safe_command;
|
||||
#[cfg(windows)]
|
||||
pub(crate) mod windows_safe_commands;
|
||||
pub(crate) use powershell_parser::try_parse_powershell_ast_commands;
|
||||
pub(crate) use powershell_tree_sitter::try_parse_powershell_commands;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::command_safety::powershell_parser::PowershellParseOutcome;
|
||||
use crate::command_safety::powershell_parser::parse_with_powershell_ast;
|
||||
use crate::command_safety::try_parse_powershell_commands;
|
||||
use std::path::Path;
|
||||
|
||||
/// On Windows, we conservatively allow only clearly read-only PowerShell invocations
|
||||
@@ -92,14 +91,8 @@ fn parse_powershell_invocation(executable: &str, args: &[String]) -> Option<Vec<
|
||||
|
||||
/// Tokenizes an inline PowerShell script and delegates to the command splitter.
|
||||
/// Examples of when this is called: pwsh.exe -Command '<script>' or pwsh.exe -Command:<script>
|
||||
fn parse_powershell_script(executable: &str, script: &str) -> Option<Vec<Vec<String>>> {
|
||||
if let PowershellParseOutcome::Commands(commands) =
|
||||
parse_with_powershell_ast(executable, script)
|
||||
{
|
||||
Some(commands)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
fn parse_powershell_script(_executable: &str, script: &str) -> Option<Vec<Vec<String>>> {
|
||||
try_parse_powershell_commands(script)
|
||||
}
|
||||
|
||||
/// Returns true when the executable name is one of the supported PowerShell binaries.
|
||||
@@ -305,12 +298,6 @@ mod tests {
|
||||
"Get-Content foo.rs | Select-Object -Skip 200".to_string()
|
||||
]));
|
||||
|
||||
assert!(is_safe_command_windows(&[
|
||||
pwsh.clone(),
|
||||
"-Command".to_string(),
|
||||
"(Get-Content foo.rs -Raw)".to_string()
|
||||
]));
|
||||
|
||||
assert!(is_safe_command_windows(&[
|
||||
pwsh,
|
||||
"-Command".to_string(),
|
||||
@@ -469,13 +456,6 @@ mod tests {
|
||||
"ls @(calc.exe)"
|
||||
])));
|
||||
|
||||
// Unsupported constructs that the AST parser refuses (no fallback to manual splitting).
|
||||
assert!(!is_safe_command_windows(&vec_str(&[
|
||||
"powershell.exe",
|
||||
"-Command",
|
||||
"ls && pwd"
|
||||
])));
|
||||
|
||||
// Sub-expressions are rejected even if they contain otherwise safe commands.
|
||||
assert!(!is_safe_command_windows(&vec_str(&[
|
||||
"powershell.exe",
|
||||
@@ -520,34 +500,4 @@ mod tests {
|
||||
"Write-Output \"foo $bar\""
|
||||
])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_invoked_powershell_variant_for_parsing() {
|
||||
if !cfg!(windows) {
|
||||
return;
|
||||
}
|
||||
|
||||
let chain = "pwd && ls";
|
||||
assert!(
|
||||
!is_safe_command_windows(&vec_str(&[
|
||||
"powershell.exe",
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
chain,
|
||||
])),
|
||||
"`{chain}` is not recognized by powershell.exe"
|
||||
);
|
||||
|
||||
if let Some(pwsh) = try_find_pwsh_executable_blocking() {
|
||||
assert!(
|
||||
is_safe_command_windows(&[
|
||||
pwsh.as_path().to_str().unwrap().into(),
|
||||
"-NoProfile".to_string(),
|
||||
"-Command".to_string(),
|
||||
chain.to_string(),
|
||||
]),
|
||||
"`{chain}` should be considered safe to pwsh.exe"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::path::PathBuf;
|
||||
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
use crate::command_safety::try_parse_powershell_ast_commands;
|
||||
use crate::command_safety::try_parse_powershell_commands;
|
||||
use crate::shell_detect::ShellType;
|
||||
use crate::shell_detect::detect_shell_type;
|
||||
@@ -74,13 +73,13 @@ pub fn extract_powershell_command(command: &[String]) -> Option<(&str, &str)> {
|
||||
/// 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.
|
||||
/// `-Command`/`-c` body from a PowerShell invocation we already recognize, then lowers the
|
||||
/// script in-process.
|
||||
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)
|
||||
let (_, script) = extract_powershell_command(command)?;
|
||||
try_parse_powershell_commands(script)
|
||||
}
|
||||
|
||||
/// Parse literal PowerShell commands without starting a PowerShell executable.
|
||||
|
||||
Reference in New Issue
Block a user