Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
89616178a6 fix(windows): clear stale protected ACLs in full access 2026-04-23 15:07:17 -07:00
4 changed files with 93 additions and 0 deletions

View File

@@ -350,6 +350,13 @@ pub(crate) async fn execute_exec_request(
arg0,
};
#[cfg(target_os = "windows")]
maybe_cleanup_stale_windows_sandbox_denies(
sandbox_policy,
params.windows_sandbox_level,
&params.cwd,
)?;
let start = Instant::now();
let raw_output_result = exec(
params,
@@ -379,6 +386,30 @@ fn extract_create_process_as_user_error_code(err: &str) -> Option<String> {
}
}
#[cfg(target_os = "windows")]
fn maybe_cleanup_stale_windows_sandbox_denies(
sandbox_policy: &SandboxPolicy,
windows_sandbox_level: WindowsSandboxLevel,
cwd: &Path,
) -> Result<()> {
if !matches!(windows_sandbox_level, WindowsSandboxLevel::Elevated)
|| !matches!(
sandbox_policy,
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
)
{
return Ok(());
}
let codex_home = crate::config::find_codex_home().map_err(|err| {
CodexErr::Io(io::Error::other(format!(
"windows sandbox: failed to resolve codex_home for ACL cleanup: {err}"
)))
})?;
crate::windows_sandbox::cleanup_stale_protected_write_denies(cwd, codex_home.as_ref())
.map_err(|err| CodexErr::Io(io::Error::other(format!("windows sandbox: {err}"))))
}
#[cfg(target_os = "windows")]
fn windowsapps_path_kind(path: &str) -> &'static str {
let lower = path.to_ascii_lowercase();

View File

@@ -192,6 +192,22 @@ pub fn run_elevated_setup(
)
}
#[cfg(target_os = "windows")]
pub fn cleanup_stale_protected_write_denies(
command_cwd: &Path,
codex_home: &Path,
) -> anyhow::Result<()> {
codex_windows_sandbox::cleanup_stale_protected_write_denies(command_cwd, codex_home)
}
#[cfg(not(target_os = "windows"))]
pub fn cleanup_stale_protected_write_denies(
_command_cwd: &Path,
_codex_home: &Path,
) -> anyhow::Result<()> {
anyhow::bail!("Windows sandbox ACL cleanup is only supported on Windows")
}
#[cfg(not(target_os = "windows"))]
pub fn run_elevated_setup(
_policy: &SandboxPolicy,

View File

@@ -116,6 +116,8 @@ pub use setup::SandboxSetupRequest;
#[cfg(target_os = "windows")]
pub use setup::SetupRootOverrides;
#[cfg(target_os = "windows")]
pub use setup::cleanup_stale_protected_write_denies;
#[cfg(target_os = "windows")]
pub use setup::run_elevated_setup;
#[cfg(target_os = "windows")]
pub use setup::run_setup_refresh;

View File

@@ -10,11 +10,16 @@ use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use crate::acl::revoke_ace;
use crate::allow::AllowDenyPaths;
use crate::allow::compute_allow_paths;
use crate::cap::load_or_create_cap_sids;
use crate::cap::workspace_cap_sid_for_cwd;
use crate::convert_string_sid_to_sid;
use crate::helper_materialization::helper_bin_dir;
use crate::logging::log_note;
use crate::path_normalization::canonical_path_key;
use crate::path_normalization::canonicalize_path;
use crate::policy::SandboxPolicy;
use crate::setup_error::SetupErrorCode;
use crate::setup_error::SetupFailure;
@@ -29,6 +34,8 @@ use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::HLOCAL;
use windows_sys::Win32::Foundation::LocalFree;
use windows_sys::Win32::Security::AllocateAndInitializeSid;
use windows_sys::Win32::Security::CheckTokenMembership;
use windows_sys::Win32::Security::FreeSid;
@@ -142,6 +149,43 @@ pub fn run_setup_refresh_with_extra_read_roots(
)
}
pub fn cleanup_stale_protected_write_denies(command_cwd: &Path, codex_home: &Path) -> Result<()> {
let caps = load_or_create_cap_sids(codex_home)?;
let cap_psid = unsafe {
convert_string_sid_to_sid(&caps.workspace)
.ok_or_else(|| anyhow!("convert capability SID {} failed", caps.workspace))?
};
let workspace_sid = workspace_cap_sid_for_cwd(codex_home, command_cwd)?;
let workspace_psid = unsafe {
convert_string_sid_to_sid(&workspace_sid)
.ok_or_else(|| anyhow!("convert workspace capability SID {workspace_sid} failed"))?
};
let canonical_command_cwd = canonicalize_path(command_cwd);
for protected_subdir in [".git", ".codex", ".agents"] {
let protected_path = canonical_command_cwd.join(protected_subdir);
if !protected_path.exists() {
continue;
}
unsafe {
revoke_ace(&protected_path, workspace_psid);
revoke_ace(&protected_path, cap_psid);
}
}
unsafe {
if !cap_psid.is_null() {
LocalFree(cap_psid as HLOCAL);
}
if !workspace_psid.is_null() {
LocalFree(workspace_psid as HLOCAL);
}
}
Ok(())
}
fn run_setup_refresh_inner(
request: SandboxSetupRequest<'_>,
overrides: SetupRootOverrides,