Skip to content

Conversation

@bolinfest
Copy link
Collaborator

Previous to this PR, we used a hand-rolled PowerShell parser in windows_safe_commands.rs to take a &str of PowerShell script see if it is equivalent to a list of execvp(3) invocations, and if so, we then test each using is_safe_powershell_command() to determine if the overall command is safe:

/// 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(script: &str) -> Option<Vec<Vec<String>>> {
let tokens = shlex_split(script)?;
split_into_commands(tokens)
}
/// Splits tokens into pipeline segments while ensuring no unsafe separators slip through.
/// e.g. Get-ChildItem | Measure-Object -> [['Get-ChildItem'], ['Measure-Object']]
fn split_into_commands(tokens: Vec<String>) -> Option<Vec<Vec<String>>> {

Unfortunately, our PowerShell parser did not recognize @(...) as a special construct, so it was treated as an ordinary token. This meant that the following would erroneously be considered "safe:"

ls @(calc.exe)

The fix introduced in this PR is to do something comparable what we do for Bash/Zsh, which is to use a "proper" parser to derive the list of execvp(3) calls. For Bash/Zsh, we rely on https://crates.io/crates/tree-sitter-bash, but there does not appear to be a crate of comparable quality for parsing PowerShell statically (https://github.com/airbus-cert/tree-sitter-powershell/ is the best thing I found).

Instead, in this PR, we use a PowerShell script to parse the input PowerShell program to produce the AST.

PowershellParseOutcome::Failed
}

fn powershell_candidates() -> &'static [&'static str] {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use shell detection here?

@bolinfest bolinfest force-pushed the bolinfest-parse-powershell branch from 2941ceb to 7bf5726 Compare December 11, 2025 19:00
bolinfest added a commit that referenced this pull request Dec 12, 2025
…7893)

I am trying to tighten up some of our logic around PowerShell over in
#7607 and it would be helpful to be
more precise about `pwsh.exe` versus `powershell.exe`, as they do not
accept the exact same input language.

To that end, this PR introduces utilities for detecting each on the
system. I think we also want to update `get_user_shell_path()` to return
PowerShell instead of `None` on Windows, but we'll consider that in a
separate PR since it may require more testing.
@bolinfest bolinfest force-pushed the bolinfest-parse-powershell branch 6 times, most recently from 7a03f68 to 7c88bee Compare December 12, 2025 06:56
change json depth from 3 to 10

move the powershell script to a separate file

remove unnecessary operator checks

use pwsh vs powershell consistently

clean up lint warnings
@bolinfest bolinfest force-pushed the bolinfest-parse-powershell branch from 7c88bee to 62c9e30 Compare December 12, 2025 07:03
"-Command",
"Get-Item foo.rs | Select-Object Length",
])));
let Some(pwsh) = try_find_pwsh_executable_blocking() else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - maybe something for me to fix since it's pre-existing. This should probably be multiple tests not a bunch of asserts in a single test

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks: it would be great if you could help improve this to your satisfaction post-merge!

commands
.iter()
.all(|cmd| is_safe_powershell_command(cmd.as_slice()));
.all(|cmd| is_safe_powershell_command(cmd.as_slice()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not a big concern in practice, but if the new powershell-based parser fails to parse an otherwise known-safe command, we would reject it. You might consider allowing commands that we know for sure are safe, based on our allow-list, even if the AST-based parser fails

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the PowerShell-based parser fails to parse the input, then I don't think there's a reason we can (or should) assume it's safe.

@bolinfest bolinfest merged commit 9009490 into main Dec 12, 2025
45 of 47 checks passed
@bolinfest bolinfest deleted the bolinfest-parse-powershell branch December 12, 2025 21:06
@github-actions github-actions bot locked and limited conversation to collaborators Dec 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants