Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
5a9ee2fa07 Improve approval prefixes for Windows executables 2026-04-06 13:11:56 -07:00
2 changed files with 142 additions and 2 deletions

View File

@@ -671,7 +671,9 @@ fn try_derive_execpolicy_amendment_for_prompt_rules(
RuleMatch::HeuristicsRuleMatch {
command,
decision: Decision::Prompt,
} => Some(ExecPolicyAmendment::from(command.clone())),
} => Some(ExecPolicyAmendment::from(
simplify_auto_execpolicy_amendment(command),
)),
_ => None,
})
}
@@ -692,11 +694,92 @@ fn try_derive_execpolicy_amendment_for_allow_rules(
RuleMatch::HeuristicsRuleMatch {
command,
decision: Decision::Allow,
} => Some(ExecPolicyAmendment::from(command.clone())),
} => Some(ExecPolicyAmendment::from(
simplify_auto_execpolicy_amendment(command),
)),
_ => None,
})
}
fn simplify_auto_execpolicy_amendment(command: &[String]) -> Vec<String> {
if !is_windows_absolute_executable_path(command.first().map(String::as_str).unwrap_or_default())
{
return command.to_vec();
}
let Some(program) = command.first() else {
return Vec::new();
};
if is_shell_like_program(program) {
return command.to_vec();
}
let mut prefix = vec![program.clone()];
for token in command.iter().skip(1) {
if token.starts_with('-')
|| token.contains(['\\', '/'])
|| token.starts_with('.')
|| token.starts_with('~')
|| token.chars().all(|ch| ch.is_ascii_digit())
{
break;
}
prefix.push(token.clone());
}
if prefix.len() > 1 {
prefix
} else {
command.to_vec()
}
}
fn is_windows_absolute_executable_path(program: &str) -> bool {
let program = program.trim();
let has_drive_prefix = matches!(
program.as_bytes(),
[drive, b':', slash, ..]
if drive.is_ascii_alphabetic() && matches!(slash, b'\\' | b'/')
);
let is_unc = program.starts_with("\\\\");
(has_drive_prefix || is_unc)
&& program
.rsplit(['\\', '/'])
.next()
.is_some_and(|name| name.contains('.'))
}
fn is_shell_like_program(program: &str) -> bool {
let base = program
.rsplit(['\\', '/'])
.next()
.unwrap_or(program)
.to_ascii_lowercase();
matches!(
base.as_str(),
"bash"
| "bash.exe"
| "sh"
| "sh.exe"
| "zsh"
| "zsh.exe"
| "cmd"
| "cmd.exe"
| "powershell"
| "powershell.exe"
| "pwsh"
| "pwsh.exe"
| "python"
| "python.exe"
| "python3"
| "python3.exe"
| "node"
| "node.exe"
| "deno"
| "deno.exe"
)
}
fn derive_requested_execpolicy_amendment_from_prefix_rule(
prefix_rule: Option<&Vec<String>>,
matched_rules: &[RuleMatch],

View File

@@ -1208,6 +1208,63 @@ async fn proposed_execpolicy_amendment_is_present_for_single_command_without_pol
.await;
}
#[tokio::test]
async fn proposed_execpolicy_amendment_compacts_windows_absolute_executable_paths() {
assert_exec_approval_requirement_for_command(
ExecApprovalRequirementScenario {
policy_src: None,
command: vec![
r"C:\Program Files\GitHub CLI\gh.exe".to_string(),
"issue".to_string(),
"view".to_string(),
"41".to_string(),
"--repo".to_string(),
"openai/codex".to_string(),
],
approval_policy: AskForApproval::UnlessTrusted,
sandbox_policy: SandboxPolicy::new_read_only_policy(),
file_system_sandbox_policy: read_only_file_system_sandbox_policy(),
sandbox_permissions: SandboxPermissions::UseDefault,
prefix_rule: None,
},
ExecApprovalRequirement::NeedsApproval {
reason: None,
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(vec![
r"C:\Program Files\GitHub CLI\gh.exe".to_string(),
"issue".to_string(),
"view".to_string(),
])),
},
)
.await;
}
#[tokio::test]
async fn proposed_execpolicy_amendment_keeps_shell_like_windows_executables_specific() {
let command = vec![
r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe".to_string(),
"-Command".to_string(),
"Write-Host hi".to_string(),
];
assert_exec_approval_requirement_for_command(
ExecApprovalRequirementScenario {
policy_src: None,
command: command.clone(),
approval_policy: AskForApproval::UnlessTrusted,
sandbox_policy: SandboxPolicy::new_read_only_policy(),
file_system_sandbox_policy: read_only_file_system_sandbox_policy(),
sandbox_permissions: SandboxPermissions::UseDefault,
prefix_rule: None,
},
ExecApprovalRequirement::NeedsApproval {
reason: None,
proposed_execpolicy_amendment: Some(ExecPolicyAmendment::new(command)),
},
)
.await;
}
#[tokio::test]
async fn proposed_execpolicy_amendment_is_omitted_when_policy_prompts() {
assert_exec_approval_requirement_for_command(