fix(security): enhance shell redirection blocking in security policy (#521)
* fix(security): enhance shell redirection blocking in security policy Block process substitution (<(...) and >(...)) and tee command in is_command_allowed() to close shell escape vectors that bypass existing redirect and subshell checks. Closes #514 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: apply rustfmt to providers/mod.rs Fix pre-existing formatting issue from main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bc18b8d3c6
commit
a2986db3d6
1 changed files with 30 additions and 1 deletions
|
|
@ -350,7 +350,12 @@ impl SecurityPolicy {
|
||||||
|
|
||||||
// Block subshell/expansion operators — these allow hiding arbitrary
|
// Block subshell/expansion operators — these allow hiding arbitrary
|
||||||
// commands inside an allowed command (e.g. `echo $(rm -rf /)`)
|
// commands inside an allowed command (e.g. `echo $(rm -rf /)`)
|
||||||
if command.contains('`') || command.contains("$(") || command.contains("${") {
|
if command.contains('`')
|
||||||
|
|| command.contains("$(")
|
||||||
|
|| command.contains("${")
|
||||||
|
|| command.contains("<(")
|
||||||
|
|| command.contains(">(")
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,6 +364,15 @@ impl SecurityPolicy {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block `tee` — it can write to arbitrary files, bypassing the
|
||||||
|
// redirect check above (e.g. `echo secret | tee /etc/crontab`)
|
||||||
|
if command
|
||||||
|
.split_whitespace()
|
||||||
|
.any(|w| w == "tee" || w.ends_with("/tee"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Block background command chaining (`&`), which can hide extra
|
// Block background command chaining (`&`), which can hide extra
|
||||||
// sub-commands and outlive timeout expectations. Keep `&&` allowed.
|
// sub-commands and outlive timeout expectations. Keep `&&` allowed.
|
||||||
if contains_single_ampersand(command) {
|
if contains_single_ampersand(command) {
|
||||||
|
|
@ -988,6 +1002,21 @@ mod tests {
|
||||||
assert!(!p.is_command_allowed("echo ${IFS}cat${IFS}/etc/passwd"));
|
assert!(!p.is_command_allowed("echo ${IFS}cat${IFS}/etc/passwd"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_injection_tee_blocked() {
|
||||||
|
let p = default_policy();
|
||||||
|
assert!(!p.is_command_allowed("echo secret | tee /etc/crontab"));
|
||||||
|
assert!(!p.is_command_allowed("ls | /usr/bin/tee outfile"));
|
||||||
|
assert!(!p.is_command_allowed("tee file.txt"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_injection_process_substitution_blocked() {
|
||||||
|
let p = default_policy();
|
||||||
|
assert!(!p.is_command_allowed("cat <(echo pwned)"));
|
||||||
|
assert!(!p.is_command_allowed("ls >(cat /etc/passwd)"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command_env_var_prefix_with_allowed_cmd() {
|
fn command_env_var_prefix_with_allowed_cmd() {
|
||||||
let p = default_policy();
|
let p = default_policy();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue