Codex Handbook
windows-sandbox-rs/src/bin/setup_main/win.rs 1192 lines
mod firewall;mod read_acl_mutex;use anyhow::Context;use anyhow::Result;use base64::Engine;use base64::engine::general_purpose::STANDARD as BASE64;use codex_otel::StatsigMetricsSettings;use codex_windows_sandbox::SETUP_VERSION;use codex_windows_sandbox::SetupErrorCode;use codex_windows_sandbox::SetupErrorReport;use codex_windows_sandbox::SetupFailure;use codex_windows_sandbox::add_deny_write_ace;use codex_windows_sandbox::canonicalize_path;use codex_windows_sandbox::convert_string_sid_to_sid;use codex_windows_sandbox::ensure_allow_mask_aces_with_inheritance;use codex_windows_sandbox::ensure_allow_write_aces;use codex_windows_sandbox::extract_setup_failure;use codex_windows_sandbox::hide_newly_created_users;use codex_windows_sandbox::install_wfp_filters;use codex_windows_sandbox::is_command_cwd_root;use codex_windows_sandbox::log_note;use codex_windows_sandbox::log_writer;use codex_windows_sandbox::path_mask_allows;use codex_windows_sandbox::sandbox_bin_dir;use codex_windows_sandbox::sandbox_dir;use codex_windows_sandbox::sandbox_secrets_dir;use codex_windows_sandbox::string_from_sid_bytes;use codex_windows_sandbox::sync_persistent_deny_read_acls;use codex_windows_sandbox::to_wide;use codex_windows_sandbox::workspace_write_cap_sid_for_root;use codex_windows_sandbox::workspace_write_root_overlaps_path;use codex_windows_sandbox::write_setup_error_report;use serde::Deserialize;use serde::Serialize;use std::collections::HashSet;use std::ffi::OsStr;use std::ffi::c_void;use std::io::Write;use std::os::windows::process::CommandExt;use std::path::Path;use std::path::PathBuf;use std::process::Command;use std::process::Stdio;use std::sync::mpsc;use windows_sys::Win32::Foundation::GetLastError;use windows_sys::Win32::Foundation::HLOCAL;use windows_sys::Win32::Foundation::LocalFree;use windows_sys::Win32::Security::ACL;use windows_sys::Win32::Security::Authorization::ConvertStringSidToSidW;use windows_sys::Win32::Security::Authorization::EXPLICIT_ACCESS_W;use windows_sys::Win32::Security::Authorization::GRANT_ACCESS;use windows_sys::Win32::Security::Authorization::SE_FILE_OBJECT;use windows_sys::Win32::Security::Authorization::SetEntriesInAclW;use windows_sys::Win32::Security::Authorization::SetNamedSecurityInfoW;use windows_sys::Win32::Security::Authorization::TRUSTEE_IS_SID;use windows_sys::Win32::Security::Authorization::TRUSTEE_W;use windows_sys::Win32::Security::CONTAINER_INHERIT_ACE;use windows_sys::Win32::Security::DACL_SECURITY_INFORMATION;use windows_sys::Win32::Security::OBJECT_INHERIT_ACE;use windows_sys::Win32::Storage::FileSystem::DELETE;use windows_sys::Win32::Storage::FileSystem::FILE_DELETE_CHILD;use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_EXECUTE;use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_READ;use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_WRITE;const DENY_ACCESS: i32 = 3;mod sandbox_users;mod setup_runtime_bin;use read_acl_mutex::acquire_read_acl_mutex;use read_acl_mutex::read_acl_mutex_exists;use sandbox_users::commit_setup_marker;use sandbox_users::prepare_setup_marker;use sandbox_users::provision_sandbox_users;use sandbox_users::resolve_sandbox_users_group_sid;use sandbox_users::resolve_sid;use sandbox_users::sid_bytes_to_psid;#[derive(Debug, Clone, Deserialize, Serialize)]struct Payload {    version: u32,    offline_username: String,    online_username: String,    codex_home: PathBuf,    command_cwd: PathBuf,    read_roots: Vec<PathBuf>,    write_roots: Vec<PathBuf>,    #[serde(default)]    deny_read_paths: Vec<PathBuf>,    #[serde(default)]    deny_write_paths: Vec<PathBuf>,    proxy_ports: Vec<u16>,    #[serde(default)]    allow_local_binding: bool,    #[serde(default)]    otel: Option<StatsigMetricsSettings>,    real_user: String,    #[serde(default)]    mode: SetupMode,    #[serde(default)]    refresh_only: bool,}#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]#[serde(rename_all = "kebab-case")]enum SetupMode {    #[default]    Full,    ProvisionOnly,    ReadAclsOnly,}fn log_line(log: &mut dyn Write, msg: &str) -> Result<()> {    let ts = chrono::Utc::now().to_rfc3339();    writeln!(log, "[{ts}] {msg}").map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperLogFailed,            format!("failed to write setup log line: {err}"),        ))    })?;    Ok(())}fn workspace_write_cap_sids_for_path(    codex_home: &Path,    command_cwd: &Path,    write_roots: &[PathBuf],    path: &Path,) -> Result<Vec<String>> {    let mut sid_strs = Vec::new();    for root in write_roots {        if workspace_write_root_overlaps_path(root, path) {            sid_strs.push(workspace_write_cap_sid_for_root(                codex_home,                command_cwd,                root,            )?);        }    }    if sid_strs.is_empty() {        if write_roots.is_empty() {            sid_strs.push(workspace_write_cap_sid_for_root(                codex_home,                command_cwd,                command_cwd,            )?);        } else {            for root in write_roots {                sid_strs.push(workspace_write_cap_sid_for_root(                    codex_home,                    command_cwd,                    root,                )?);            }        }    }    Ok(sid_strs)}fn spawn_read_acl_helper(payload: &Payload, _log: &mut dyn Write) -> Result<()> {    let mut read_payload = payload.clone();    read_payload.mode = SetupMode::ReadAclsOnly;    read_payload.refresh_only = true;    let payload_json = serde_json::to_vec(&read_payload)?;    let payload_b64 = BASE64.encode(payload_json);    let exe = std::env::current_exe().context("locate setup helper")?;    Command::new(&exe)        .arg(payload_b64)        .stdin(Stdio::null())        .stdout(Stdio::null())        .stderr(Stdio::null())        .creation_flags(0x08000000) // CREATE_NO_WINDOW        .spawn()        .context("spawn read ACL helper")?;    Ok(())}struct ReadAclSubjects<'a> {    sandbox_group_psid: *mut c_void,    rx_psids: &'a [*mut c_void],}fn apply_read_acls(    read_roots: &[PathBuf],    subjects: &ReadAclSubjects<'_>,    log: &mut dyn Write,    refresh_errors: &mut Vec<String>,    access_mask: u32,    access_label: &str,    inheritance: u32,) -> Result<()> {    for root in read_roots {        if !root.exists() {            log_line(                log,                &format!("{access_label} root {} missing; skipping", root.display()),            )?;            continue;        }        let builtin_has = read_mask_allows_or_log(            root,            subjects.rx_psids,            /*label*/ None,            access_mask,            access_label,            refresh_errors,            log,        )?;        if builtin_has {            continue;        }        let sandbox_has = read_mask_allows_or_log(            root,            &[subjects.sandbox_group_psid],            Some("sandbox_group"),            access_mask,            access_label,            refresh_errors,            log,        )?;        if sandbox_has {            continue;        }        log_line(            log,            &format!(                "granting {access_label} ACE to {} for sandbox users",                root.display()            ),        )?;        let result = unsafe {            ensure_allow_mask_aces_with_inheritance(                root,                &[subjects.sandbox_group_psid],                access_mask,                inheritance,            )        };        if let Err(err) = result {            refresh_errors.push(format!(                "grant {access_label} ACE failed on {} for sandbox_group: {err}",                root.display()            ));            log_line(                log,                &format!(                    "grant {access_label} ACE failed on {} for sandbox_group: {err}",                    root.display()                ),            )?;        }    }    Ok(())}fn read_mask_allows_or_log(    root: &Path,    psids: &[*mut c_void],    label: Option<&str>,    read_mask: u32,    access_label: &str,    refresh_errors: &mut Vec<String>,    log: &mut dyn Write,) -> Result<bool> {    match path_mask_allows(root, psids, read_mask, /*require_all_bits*/ true) {        Ok(has) => Ok(has),        Err(e) => {            let label_suffix = label                .map(|value| format!(" for {value}"))                .unwrap_or_default();            refresh_errors.push(format!(                "{access_label} mask check failed on {}{}: {}",                root.display(),                label_suffix,                e            ));            log_line(                log,                &format!(                    "{access_label} mask check failed on {}{}: {}; continuing",                    root.display(),                    label_suffix,                    e                ),            )?;            Ok(false)        }    }}fn lock_sandbox_dir(    dir: &Path,    real_user: &str,    sandbox_group_sid: &[u8],    sandbox_group_access_mode: i32,    sandbox_group_mask: u32,    real_user_mask: u32,    _log: &mut dyn Write,) -> Result<()> {    std::fs::create_dir_all(dir)?;    let system_sid = resolve_sid("SYSTEM")?;    let admins_sid = resolve_sid("Administrators")?;    let real_sid = resolve_sid(real_user)?;    let entries = [        (            sandbox_group_sid.to_vec(),            sandbox_group_mask,            sandbox_group_access_mode,        ),        (            system_sid,            FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,            GRANT_ACCESS,        ),        (            admins_sid,            FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,            GRANT_ACCESS,        ),        (real_sid, real_user_mask, GRANT_ACCESS),    ];    unsafe {        let mut eas: Vec<EXPLICIT_ACCESS_W> = Vec::new();        let mut sids: Vec<*mut c_void> = Vec::new();        for (sid_bytes, mask, access_mode) in entries.iter().map(|(s, m, a)| (s, *m, *a)) {            let sid_str = string_from_sid_bytes(sid_bytes).map_err(anyhow::Error::msg)?;            let sid_w = to_wide(OsStr::new(&sid_str));            let mut psid: *mut c_void = std::ptr::null_mut();            if ConvertStringSidToSidW(sid_w.as_ptr(), &mut psid) == 0 {                return Err(anyhow::anyhow!(                    "ConvertStringSidToSidW failed: {}",                    GetLastError()                ));            }            sids.push(psid);            eas.push(EXPLICIT_ACCESS_W {                grfAccessPermissions: mask,                grfAccessMode: access_mode,                grfInheritance: OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,                Trustee: TRUSTEE_W {                    pMultipleTrustee: std::ptr::null_mut(),                    MultipleTrusteeOperation: 0,                    TrusteeForm: TRUSTEE_IS_SID,                    TrusteeType: TRUSTEE_IS_SID,                    ptstrName: psid as *mut u16,                },            });        }        let mut new_dacl: *mut ACL = std::ptr::null_mut();        let set = SetEntriesInAclW(            eas.len() as u32,            eas.as_ptr(),            std::ptr::null_mut(),            &mut new_dacl,        );        if set != 0 {            return Err(anyhow::anyhow!(                "SetEntriesInAclW sandbox dir failed: {set}",            ));        }        let path_w = to_wide(dir.as_os_str());        let res = SetNamedSecurityInfoW(            path_w.as_ptr() as *mut u16,            SE_FILE_OBJECT,            DACL_SECURITY_INFORMATION,            std::ptr::null_mut(),            std::ptr::null_mut(),            new_dacl,            std::ptr::null_mut(),        );        if res != 0 {            return Err(anyhow::anyhow!(                "SetNamedSecurityInfoW sandbox dir failed: {res}",            ));        }        if !new_dacl.is_null() {            LocalFree(new_dacl as HLOCAL);        }        for sid in sids {            if !sid.is_null() {                LocalFree(sid as HLOCAL);            }        }    }    Ok(())}pub fn main() -> Result<()> {    let ret = real_main();    if let Err(e) = &ret {        // Best-effort: log unexpected top-level errors.        if let Ok(codex_home) = std::env::var("CODEX_HOME") {            let sbx_dir = sandbox_dir(Path::new(&codex_home));            let _ = std::fs::create_dir_all(&sbx_dir);            if let Some(mut f) = log_writer(&sbx_dir) {                let _ = writeln!(                    f,                    "[{}] top-level error: {}",                    chrono::Utc::now().to_rfc3339(),                    e                );            }        }    }    ret}fn real_main() -> Result<()> {    let mut args = std::env::args().collect::<Vec<_>>();    if args.len() != 2 {        return Err(anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperRequestArgsFailed,            "expected payload argument",        )));    }    let payload_b64 = args.remove(1);    let payload_json = BASE64.decode(payload_b64).map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperRequestArgsFailed,            format!("failed to decode payload b64: {err}"),        ))    })?;    let payload: Payload = serde_json::from_slice(&payload_json).map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperRequestArgsFailed,            format!("failed to parse payload json: {err}"),        ))    })?;    if payload.version != SETUP_VERSION {        return Err(anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperRequestArgsFailed,            format!(                "setup version mismatch: expected {SETUP_VERSION}, got {}",                payload.version            ),        )));    }    let sbx_dir = sandbox_dir(&payload.codex_home);    std::fs::create_dir_all(&sbx_dir).map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSandboxDirCreateFailed,            format!("failed to create sandbox dir {}: {err}", sbx_dir.display()),        ))    })?;    let mut log = log_writer(&sbx_dir).ok_or_else(|| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperLogFailed,            format!("open log in {} failed", sbx_dir.display()),        ))    })?;    let result = run_setup(&payload, &mut log, &sbx_dir);    if let Err(err) = &result {        let _ = log_line(&mut log, &format!("setup error: {err:?}"));        log_note(&format!("setup error: {err:?}"), Some(sbx_dir.as_path()));        let failure = extract_setup_failure(err)            .map(|f| SetupFailure::new(f.code, f.message.clone()))            .unwrap_or_else(|| {                SetupFailure::new(SetupErrorCode::HelperUnknownError, err.to_string())            });        let report = SetupErrorReport {            code: failure.code,            message: failure.message,        };        if let Err(write_err) = write_setup_error_report(&payload.codex_home, &report) {            let _ = log_line(                &mut log,                &format!("setup error report write failed: {write_err}"),            );            log_note(                &format!("setup error report write failed: {write_err}"),                Some(sbx_dir.as_path()),            );        }    }    result}fn run_setup(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Result<()> {    let writes_setup_marker = !payload.refresh_only && payload.mode != SetupMode::ReadAclsOnly;    if writes_setup_marker {        prepare_setup_marker(&payload.codex_home, &payload.real_user)?;    }    match payload.mode {        SetupMode::ReadAclsOnly => run_read_acl_only(payload, log),        SetupMode::ProvisionOnly => run_provision_only(payload, log, sbx_dir),        SetupMode::Full => run_setup_full(payload, log, sbx_dir),    }?;    if writes_setup_marker {        commit_setup_marker(            &payload.codex_home,            &payload.offline_username,            &payload.online_username,            &payload.proxy_ports,            payload.allow_local_binding,        )?;    }    Ok(())}fn run_read_acl_only(payload: &Payload, log: &mut dyn Write) -> Result<()> {    let _read_acl_guard = match acquire_read_acl_mutex()? {        Some(guard) => guard,        None => {            log_line(log, "read ACL helper already running; skipping")?;            return Ok(());        }    };    log_line(log, "read-acl-only mode: applying read ACLs")?;    let sandbox_group_sid = resolve_sandbox_users_group_sid()?;    let sandbox_group_psid = sid_bytes_to_psid(&sandbox_group_sid)?;    let mut refresh_errors: Vec<String> = Vec::new();    if !payload.read_roots.is_empty() {        let users_sid = resolve_sid("Users")?;        let users_psid = sid_bytes_to_psid(&users_sid)?;        let auth_sid = resolve_sid("Authenticated Users")?;        let auth_psid = sid_bytes_to_psid(&auth_sid)?;        let everyone_sid = resolve_sid("Everyone")?;        let everyone_psid = sid_bytes_to_psid(&everyone_sid)?;        let rx_psids = vec![users_psid, auth_psid, everyone_psid];        let subjects = ReadAclSubjects {            sandbox_group_psid,            rx_psids: &rx_psids,        };        apply_read_acls(            &payload.read_roots,            &subjects,            log,            &mut refresh_errors,            FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,            "read",            OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE,        )?;        unsafe {            if !users_psid.is_null() {                LocalFree(users_psid as HLOCAL);            }            if !auth_psid.is_null() {                LocalFree(auth_psid as HLOCAL);            }            if !everyone_psid.is_null() {                LocalFree(everyone_psid as HLOCAL);            }        }    }    unsafe {        if !sandbox_group_psid.is_null() {            LocalFree(sandbox_group_psid as HLOCAL);        }    }    if !refresh_errors.is_empty() {        log_line(            log,            &format!("read ACL run completed with errors: {refresh_errors:?}"),        )?;        if payload.refresh_only {            anyhow::bail!("read ACL run had errors");        }    }    log_line(log, "read ACL run completed")?;    Ok(())}fn provision_and_hide_sandbox_users(    payload: &Payload,    log: &mut dyn Write,    sbx_dir: &Path,) -> Result<()> {    let provision_result = provision_sandbox_users(        &payload.codex_home,        &payload.offline_username,        &payload.online_username,        log,    );    if let Err(err) = provision_result {        if extract_setup_failure(&err).is_some() {            return Err(err);        }        return Err(anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperUserProvisionFailed,            format!("provision sandbox users failed: {err}"),        )));    }    let users = vec![        payload.offline_username.clone(),        payload.online_username.clone(),    ];    hide_newly_created_users(&users, sbx_dir);    Ok(())}fn configure_offline_sandbox_network(    payload: &Payload,    offline_sid_str: &str,    log: &mut dyn Write,) -> Result<()> {    let proxy_allowlist_result = firewall::ensure_offline_proxy_allowlist(        offline_sid_str,        &payload.proxy_ports,        payload.allow_local_binding,        log,    );    if let Err(err) = proxy_allowlist_result {        if extract_setup_failure(&err).is_some() {            return Err(err);        }        return Err(anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperFirewallRuleCreateOrAddFailed,            format!("ensure offline proxy allowlist failed: {err}"),        )));    }    let firewall_result = firewall::ensure_offline_outbound_block(offline_sid_str, log);    if let Err(err) = firewall_result {        if extract_setup_failure(&err).is_some() {            return Err(err);        }        return Err(anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperFirewallRuleCreateOrAddFailed,            format!("ensure offline outbound block failed: {err}"),        )));    }    install_wfp_filters(        &payload.codex_home,        &payload.offline_username,        payload.otel.as_ref(),        |message| {            let _ = log_line(log, message);        },    );    Ok(())}fn lock_persistent_sandbox_dirs(    payload: &Payload,    sandbox_group_sid: &[u8],    log: &mut dyn Write,) -> Result<()> {    lock_sandbox_dir(        &sandbox_dir(&payload.codex_home),        &payload.real_user,        sandbox_group_sid,        GRANT_ACCESS,        FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,        FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,        log,    )    .map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSandboxLockFailed,            format!(                "lock sandbox dir {} failed: {err}",                sandbox_dir(&payload.codex_home).display()            ),        ))    })?;    lock_sandbox_dir(        &sandbox_secrets_dir(&payload.codex_home),        &payload.real_user,        sandbox_group_sid,        DENY_ACCESS,        FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,        FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE,        log,    )    .map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSandboxLockFailed,            format!(                "lock sandbox secrets dir {} failed: {err}",                sandbox_secrets_dir(&payload.codex_home).display()            ),        ))    })?;    let legacy_users = sandbox_dir(&payload.codex_home).join("sandbox_users.json");    if legacy_users.exists() {        let _ = std::fs::remove_file(&legacy_users);    }    Ok(())}fn lock_sandbox_bin_dir(    payload: &Payload,    sandbox_group_sid: &[u8],    log: &mut dyn Write,) -> Result<()> {    lock_sandbox_dir(        &sandbox_bin_dir(&payload.codex_home),        &payload.real_user,        sandbox_group_sid,        GRANT_ACCESS,        FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,        FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE,        log,    )    .map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSandboxLockFailed,            format!(                "lock sandbox bin dir {} failed: {err}",                sandbox_bin_dir(&payload.codex_home).display()            ),        ))    })}fn run_provision_only(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Result<()> {    provision_and_hide_sandbox_users(payload, log, sbx_dir)?;    let offline_sid = resolve_sid(&payload.offline_username).map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSidResolveFailed,            format!(                "resolve SID for offline user {} failed: {err}",                payload.offline_username            ),        ))    })?;    let offline_sid_str = string_from_sid_bytes(&offline_sid).map_err(anyhow::Error::msg)?;    let sandbox_group_sid = resolve_sandbox_users_group_sid().map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSidResolveFailed,            format!("resolve sandbox users group SID failed: {err}"),        ))    })?;    configure_offline_sandbox_network(payload, &offline_sid_str, log)?;    lock_sandbox_bin_dir(payload, &sandbox_group_sid, log)?;    lock_persistent_sandbox_dirs(payload, &sandbox_group_sid, log)?;    log_note("setup provisioning binary completed", Some(sbx_dir));    Ok(())}fn run_setup_full(payload: &Payload, log: &mut dyn Write, sbx_dir: &Path) -> Result<()> {    let refresh_only = payload.refresh_only;    if !refresh_only {        provision_and_hide_sandbox_users(payload, log, sbx_dir)?;    }    let offline_sid = resolve_sid(&payload.offline_username).map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSidResolveFailed,            format!(                "resolve SID for offline user {} failed: {err}",                payload.offline_username            ),        ))    })?;    let offline_sid_str = string_from_sid_bytes(&offline_sid).map_err(anyhow::Error::msg)?;    let sandbox_group_sid = resolve_sandbox_users_group_sid().map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSidResolveFailed,            format!("resolve sandbox users group SID failed: {err}"),        ))    })?;    let sandbox_group_psid = sid_bytes_to_psid(&sandbox_group_sid).map_err(|err| {        anyhow::Error::new(SetupFailure::new(            SetupErrorCode::HelperSidResolveFailed,            format!("convert sandbox users group SID to PSID failed: {err}"),        ))    })?;    let sandbox_group_sid_str =        string_from_sid_bytes(&sandbox_group_sid).map_err(anyhow::Error::msg)?;    let mut refresh_errors: Vec<String> = Vec::new();    if !refresh_only {        configure_offline_sandbox_network(payload, &offline_sid_str, log)?;    }    // Deny-read ACEs must be present before the sandboxed command starts. Apply    // them synchronously here instead of delegating them to the background    // helper used for read grants.    let applied_deny_read_paths = unsafe {        sync_persistent_deny_read_acls(            &payload.codex_home,            &sandbox_group_sid_str,            &payload.deny_read_paths,            sandbox_group_psid,        )    }    .context("apply deny-read ACLs")?;    if !applied_deny_read_paths.is_empty() {        log_line(            log,            &format!("applied {} deny-read ACLs", applied_deny_read_paths.len()),        )?;    }    if payload.read_roots.is_empty() {        log_line(log, "no read roots to grant; skipping read ACL helper")?;    } else {        match read_acl_mutex_exists() {            Ok(true) => {                log_line(log, "read ACL helper already running; skipping spawn")?;            }            Ok(false) => {                spawn_read_acl_helper(payload, log).map_err(|err| {                    anyhow::Error::new(SetupFailure::new(                        SetupErrorCode::HelperReadAclHelperSpawnFailed,                        format!("spawn read ACL helper failed: {err}"),                    ))                })?;            }            Err(err) => {                log_line(                    log,                    &format!("read ACL mutex check failed: {err}; spawning anyway"),                )?;                spawn_read_acl_helper(payload, log).map_err(|spawn_err| {                    anyhow::Error::new(SetupFailure::new(                        SetupErrorCode::HelperReadAclHelperSpawnFailed,                        format!(                            "spawn read ACL helper failed after mutex error {err}: {spawn_err}"                        ),                    ))                })?;            }        }    }    if refresh_only {        setup_runtime_bin::ensure_codex_app_runtime_bin_readable(            sandbox_group_psid,            &mut refresh_errors,            log,        )?;    }    let write_mask =        FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE | FILE_DELETE_CHILD;    let mut grant_tasks: Vec<(PathBuf, String)> = Vec::new();    let mut seen_deny_paths: HashSet<PathBuf> = HashSet::new();    let mut seen_write_roots: HashSet<PathBuf> = HashSet::new();    let canonical_command_cwd = canonicalize_path(&payload.command_cwd);    for root in &payload.write_roots {        if !seen_write_roots.insert(root.clone()) {            continue;        }        if !root.exists() {            log_line(                log,                &format!("write root {} missing; skipping", root.display()),            )?;            continue;        }        let mut need_grant = false;        let is_command_cwd = is_command_cwd_root(root, &canonical_command_cwd);        let cap_label = if is_command_cwd {            "workspace_cap"        } else {            "root_cap"        };        let root_cap_sid_str =            workspace_write_cap_sid_for_root(&payload.codex_home, &payload.command_cwd, root)?;        let root_cap_psid = unsafe {            convert_string_sid_to_sid(&root_cap_sid_str)                .ok_or_else(|| anyhow::anyhow!("convert write root capability SID failed"))?        };        for (label, psid) in [            ("sandbox_group", sandbox_group_psid),            (cap_label, root_cap_psid),        ] {            let has =                match path_mask_allows(root, &[psid], write_mask, /*require_all_bits*/ true) {                    Ok(h) => h,                    Err(e) => {                        refresh_errors.push(format!(                            "write mask check failed on {} for {label}: {}",                            root.display(),                            e                        ));                        log_line(                            log,                            &format!(                                "write mask check failed on {} for {label}: {}; continuing",                                root.display(),                                e                            ),                        )?;                        false                    }                };            if !has {                need_grant = true;            }        }        unsafe {            LocalFree(root_cap_psid as HLOCAL);        }        if need_grant {            log_line(                log,                &format!(                    "granting write ACE to {} for sandbox group and capability SID",                    root.display()                ),            )?;            grant_tasks.push((root.clone(), root_cap_sid_str));        }    }    let (tx, rx) = mpsc::channel::<(PathBuf, Result<bool>)>();    std::thread::scope(|scope| {        for (root, root_cap_sid_str) in grant_tasks {            let sid_strings = vec![sandbox_group_sid_str.clone(), root_cap_sid_str];            let tx = tx.clone();            scope.spawn(move || {                // Convert SID strings to psids locally in this thread.                let mut psids: Vec<*mut c_void> = Vec::new();                for sid_str in &sid_strings {                    if let Some(psid) = unsafe { convert_string_sid_to_sid(sid_str) } {                        psids.push(psid);                    } else {                        let _ = tx.send((root.clone(), Err(anyhow::anyhow!("convert SID failed"))));                        return;                    }                }                let res = unsafe { ensure_allow_write_aces(&root, &psids) };                for psid in psids {                    unsafe {                        LocalFree(psid as HLOCAL);                    }                }                let _ = tx.send((root, res));            });        }        drop(tx);        for (root, res) in rx {            match res {                Ok(_) => {}                Err(e) => {                    refresh_errors.push(format!("write ACE failed on {}: {}", root.display(), e));                    if log_line(                        log,                        &format!("write ACE grant failed on {}: {}", root.display(), e),                    )                    .is_err()                    {                        // ignore log errors inside scoped thread                    }                }            }        }    });    for path in &payload.deny_write_paths {        if !seen_deny_paths.insert(path.clone()) {            continue;        }        // These are deny-write carveouts, not deny-read paths. They may come from explicit        // read-only-under-a-writable-root carveouts in the transformed sandbox policy, or from        // legacy protected children such as `.git`, `.codex`, and `.agents`.        //        // Deny ACEs attach to filesystem objects; if an explicit policy carveout does not exist        // during setup, the sandbox could otherwise create it later under a writable parent and        // bypass the carveout. Materialize missing carveouts as directories so the deny-write ACL        // is present before the command starts. Legacy protected children are filtered before        // payload creation, so this should not create sentinel directories in a workspace.        if !path.exists() {            std::fs::create_dir_all(path)                .with_context(|| format!("failed to create deny-write path {}", path.display()))?;        }        let deny_sid_strs = workspace_write_cap_sids_for_path(            &payload.codex_home,            &payload.command_cwd,            &payload.write_roots,            path,        )?;        for deny_sid_str in deny_sid_strs {            let deny_psid = unsafe {                convert_string_sid_to_sid(&deny_sid_str)                    .ok_or_else(|| anyhow::anyhow!("convert deny capability SID failed"))?            };            match unsafe { add_deny_write_ace(path, deny_psid) } {                Ok(true) => {                    log_line(                        log,                        &format!("applied deny ACE to protect {}", path.display()),                    )?;                }                Ok(false) => {}                Err(err) => {                    refresh_errors.push(format!("deny ACE failed on {}: {err}", path.display()));                    log_line(                        log,                        &format!("deny ACE failed on {}: {err}", path.display()),                    )?;                }            }            unsafe {                LocalFree(deny_psid as HLOCAL);            }        }    }    lock_sandbox_bin_dir(payload, &sandbox_group_sid, log)?;    if refresh_only {        log_line(            log,            &format!(                "setup refresh: processed {} write roots (read roots delegated); errors={:?}",                payload.write_roots.len(),                refresh_errors            ),        )?;    }    if !refresh_only {        lock_persistent_sandbox_dirs(payload, &sandbox_group_sid, log)?;    }    unsafe {        if !sandbox_group_psid.is_null() {            LocalFree(sandbox_group_psid as HLOCAL);        }    }    if refresh_only && !refresh_errors.is_empty() {        log_line(            log,            &format!("setup refresh completed with errors: {refresh_errors:?}"),        )?;        anyhow::bail!("setup refresh had errors");    }    log_note("setup binary completed", Some(sbx_dir));    Ok(())}#[cfg(test)]mod tests {    use super::Payload;    use super::SETUP_VERSION;    use super::workspace_write_cap_sids_for_path;    use codex_otel::StatsigMetricsSettings;    use codex_windows_sandbox::load_or_create_cap_sids;    use codex_windows_sandbox::workspace_write_cap_sid_for_root;    use pretty_assertions::assert_eq;    use serde_json::json;    use std::fs;    fn payload_json() -> serde_json::Value {        json!({            "version": SETUP_VERSION,            "offline_username": "CodexSandboxOffline",            "online_username": "CodexSandboxOnline",            "codex_home": "C:\\codex-home",            "command_cwd": "C:\\workspace",            "read_roots": [],            "write_roots": [],            "proxy_ports": [],            "real_user": "User",        })    }    #[test]    fn payload_defaults_otel_absent() {        let payload: Payload = serde_json::from_value(payload_json()).expect("payload");        assert_eq!(payload.otel, None);    }    #[test]    fn payload_accepts_provision_only_mode() {        let mut payload = payload_json();        payload["mode"] = json!("provision-only");        let payload: Payload = serde_json::from_value(payload).expect("payload");        assert_eq!(payload.mode, super::SetupMode::ProvisionOnly);    }    #[test]    fn payload_accepts_otel_settings() {        let mut payload = payload_json();        payload["otel"] = json!({            "environment": "prod",        });        let payload: Payload = serde_json::from_value(payload).expect("payload");        assert_eq!(            payload.otel,            Some(StatsigMetricsSettings {                environment: "prod".to_string(),            })        );    }    #[test]    fn deny_path_under_active_root_uses_only_matching_root_sid() {        let temp = tempfile::tempdir().expect("tempdir");        let codex_home = temp.path().join("codex-home");        let workspace = temp.path().join("workspace");        let active_root = temp.path().join("active-root");        let stale_root = temp.path().join("stale-root");        let deny_path = active_root.join("protected");        fs::create_dir_all(&codex_home).expect("create codex home");        fs::create_dir_all(&workspace).expect("create workspace");        fs::create_dir_all(&active_root).expect("create active root");        fs::create_dir_all(&stale_root).expect("create stale root");        fs::create_dir_all(&deny_path).expect("create deny path");        let stale_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &stale_root)            .expect("stale sid");        let active_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &active_root)            .expect("active sid");        let workspace_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &workspace)            .expect("workspace sid");        let caps = load_or_create_cap_sids(&codex_home).expect("load caps");        let deny_sids = workspace_write_cap_sids_for_path(            &codex_home,            &workspace,            &[workspace.clone(), active_root],            &deny_path,        )        .expect("deny sids");        assert_eq!(deny_sids, vec![active_sid]);        assert!(!deny_sids.contains(&workspace_sid));        assert!(!deny_sids.contains(&stale_sid));        assert!(!deny_sids.contains(&caps.workspace));    }    #[test]    fn deny_path_outside_active_roots_falls_back_to_all_active_root_sids() {        let temp = tempfile::tempdir().expect("tempdir");        let codex_home = temp.path().join("codex-home");        let workspace = temp.path().join("workspace");        let active_root = temp.path().join("active-root");        let stale_root = temp.path().join("stale-root");        let deny_path = temp.path().join("outside-deny");        fs::create_dir_all(&codex_home).expect("create codex home");        fs::create_dir_all(&workspace).expect("create workspace");        fs::create_dir_all(&active_root).expect("create active root");        fs::create_dir_all(&stale_root).expect("create stale root");        fs::create_dir_all(&deny_path).expect("create deny path");        let stale_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &stale_root)            .expect("stale sid");        let active_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &active_root)            .expect("active sid");        let workspace_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &workspace)            .expect("workspace sid");        let caps = load_or_create_cap_sids(&codex_home).expect("load caps");        let deny_sids = workspace_write_cap_sids_for_path(            &codex_home,            &workspace,            &[workspace.clone(), active_root],            &deny_path,        )        .expect("deny sids");        assert_eq!(deny_sids.len(), 2);        assert!(deny_sids.contains(&workspace_sid));        assert!(deny_sids.contains(&active_sid));        assert!(!deny_sids.contains(&stale_sid));        assert!(!deny_sids.contains(&caps.workspace));    }    #[test]    fn deny_path_includes_nested_active_root_sid() {        let temp = tempfile::tempdir().expect("tempdir");        let codex_home = temp.path().join("codex-home");        let workspace = temp.path().join("workspace");        let protected_dir = workspace.join(".codex");        let nested_root = protected_dir.join("nested-root");        fs::create_dir_all(&codex_home).expect("create codex home");        fs::create_dir_all(&workspace).expect("create workspace");        fs::create_dir_all(&nested_root).expect("create nested root");        let workspace_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &workspace)            .expect("workspace sid");        let nested_sid = workspace_write_cap_sid_for_root(&codex_home, &workspace, &nested_root)            .expect("nested sid");        let deny_sids = workspace_write_cap_sids_for_path(            &codex_home,            &workspace,            &[workspace.clone(), nested_root],            &protected_dir,        )        .expect("deny sids");        assert_eq!(deny_sids, vec![workspace_sid, nested_sid]);    }}