Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
cbeff282a1 Include PATH dirs in Windows sandbox read roots 2026-04-07 10:38:39 -07:00
2 changed files with 93 additions and 10 deletions

View File

@@ -136,7 +136,7 @@ pub fn require_logon_sandbox_creds(
proxy_enforced: bool,
) -> Result<SandboxCreds> {
let sandbox_dir = crate::setup::sandbox_dir(codex_home);
let needed_read = gather_read_roots(command_cwd, policy, codex_home);
let needed_read = gather_read_roots(command_cwd, policy, codex_home, env_map);
let needed_write = gather_write_roots(policy, policy_cwd, command_cwd, env_map);
let network_identity = SandboxNetworkIdentity::from_policy(policy, proxy_enforced);
let desired_offline_proxy_settings =

View File

@@ -124,7 +124,7 @@ pub fn run_setup_refresh_with_extra_read_roots(
extra_read_roots: Vec<PathBuf>,
proxy_enforced: bool,
) -> Result<()> {
let mut read_roots = gather_read_roots(command_cwd, policy, codex_home);
let mut read_roots = gather_read_roots(command_cwd, policy, codex_home, env_map);
read_roots.extend(extra_read_roots);
run_setup_refresh_inner(
SandboxSetupRequest {
@@ -311,6 +311,32 @@ fn canonical_existing(paths: &[PathBuf]) -> Vec<PathBuf> {
.collect()
}
fn env_path_value<'a>(env_map: &'a HashMap<String, String>, key: &str) -> Option<&'a str> {
env_map
.get(key)
.or_else(|| {
env_map
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(key))
.map(|(_, v)| v)
})
.map(String::as_str)
}
fn env_path_read_roots(env_map: &HashMap<String, String>) -> Vec<PathBuf> {
let Some(path) = env_path_value(env_map, "PATH") else {
return Vec::new();
};
path.split(';')
.map(str::trim)
.filter(|entry| !entry.is_empty())
.map(|entry| entry.trim_matches('"'))
.map(PathBuf::from)
.filter(|path| path.is_absolute())
.collect()
}
fn profile_read_roots(user_profile: &Path) -> Vec<PathBuf> {
let entries = match std::fs::read_dir(user_profile) {
Ok(entries) => entries,
@@ -347,6 +373,7 @@ fn gather_legacy_full_read_roots(
command_cwd: &Path,
policy: &SandboxPolicy,
codex_home: &Path,
env_map: &HashMap<String, String>,
) -> Vec<PathBuf> {
let mut roots = gather_helper_read_roots(codex_home);
roots.extend(
@@ -354,6 +381,7 @@ fn gather_legacy_full_read_roots(
.iter()
.map(PathBuf::from),
);
roots.extend(env_path_read_roots(env_map));
if let Ok(up) = std::env::var("USERPROFILE") {
roots.extend(profile_read_roots(Path::new(&up)));
}
@@ -370,6 +398,7 @@ fn gather_restricted_read_roots(
command_cwd: &Path,
policy: &SandboxPolicy,
codex_home: &Path,
env_map: &HashMap<String, String>,
) -> Vec<PathBuf> {
let mut roots = gather_helper_read_roots(codex_home);
if policy.include_platform_defaults() {
@@ -379,6 +408,7 @@ fn gather_restricted_read_roots(
.map(PathBuf::from),
);
}
roots.extend(env_path_read_roots(env_map));
roots.extend(
policy
.get_readable_roots_with_cwd(command_cwd)
@@ -392,11 +422,12 @@ pub(crate) fn gather_read_roots(
command_cwd: &Path,
policy: &SandboxPolicy,
codex_home: &Path,
env_map: &HashMap<String, String>,
) -> Vec<PathBuf> {
if policy.has_full_disk_read_access() {
gather_legacy_full_read_roots(command_cwd, policy, codex_home)
gather_legacy_full_read_roots(command_cwd, policy, codex_home, env_map)
} else {
gather_restricted_read_roots(command_cwd, policy, codex_home)
gather_restricted_read_roots(command_cwd, policy, codex_home, env_map)
}
}
@@ -767,7 +798,12 @@ fn build_payload_roots(
let mut read_roots = if let Some(roots) = overrides.read_roots {
canonical_existing(&roots)
} else {
gather_read_roots(request.command_cwd, request.policy, request.codex_home)
gather_read_roots(
request.command_cwd,
request.policy,
request.codex_home,
request.env_map,
)
};
let write_root_set: HashSet<PathBuf> = write_roots.iter().cloned().collect();
read_roots.retain(|root| !write_root_set.contains(root));
@@ -1001,14 +1037,57 @@ mod tests {
let command_cwd = tmp.path().join("workspace");
fs::create_dir_all(&command_cwd).expect("create workspace");
let policy = SandboxPolicy::new_read_only_policy();
let env_map = HashMap::new();
let roots = gather_read_roots(&command_cwd, &policy, &codex_home);
let roots = gather_read_roots(&command_cwd, &policy, &codex_home, &env_map);
let expected =
dunce::canonicalize(helper_bin_dir(&codex_home)).expect("canonical helper dir");
assert!(roots.contains(&expected));
}
#[test]
fn gather_read_roots_includes_existing_absolute_path_entries() {
let tmp = TempDir::new().expect("tempdir");
let codex_home = tmp.path().join("codex-home");
let command_cwd = tmp.path().join("workspace");
let path_tool_dir = tmp
.path()
.join("Users")
.join("dev")
.join("AppData")
.join("Roaming")
.join("fnm")
.join("node");
let missing_path_dir = tmp.path().join("missing-path-entry");
fs::create_dir_all(&command_cwd).expect("create workspace");
fs::create_dir_all(&path_tool_dir).expect("create path tool dir");
let policy = SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: Vec::new(),
},
network_access: false,
};
let mut env_map = HashMap::new();
env_map.insert(
"Path".to_string(),
format!(
"\"{}\";relative-tool-dir;{}",
path_tool_dir.display(),
missing_path_dir.display()
),
);
let roots = gather_read_roots(&command_cwd, &policy, &codex_home, &env_map);
let expected_path_tool =
dunce::canonicalize(&path_tool_dir).expect("canonical path tool dir");
assert!(roots.contains(&expected_path_tool));
assert!(!roots.contains(&PathBuf::from("relative-tool-dir")));
assert!(!roots.contains(&missing_path_dir));
}
#[test]
fn restricted_read_roots_skip_platform_defaults_when_disabled() {
let tmp = TempDir::new().expect("tempdir");
@@ -1028,7 +1107,8 @@ mod tests {
network_access: false,
};
let roots = gather_read_roots(&command_cwd, &policy, &codex_home);
let env_map = HashMap::new();
let roots = gather_read_roots(&command_cwd, &policy, &codex_home, &env_map);
let expected_helper =
dunce::canonicalize(helper_bin_dir(&codex_home)).expect("canonical helper dir");
let expected_cwd = dunce::canonicalize(&command_cwd).expect("canonical workspace");
@@ -1059,7 +1139,8 @@ mod tests {
network_access: false,
};
let roots = gather_read_roots(&command_cwd, &policy, &codex_home);
let env_map = HashMap::new();
let roots = gather_read_roots(&command_cwd, &policy, &codex_home, &env_map);
assert!(
canonical_windows_platform_default_roots()
@@ -1090,7 +1171,8 @@ mod tests {
exclude_slash_tmp: true,
};
let roots = gather_read_roots(&command_cwd, &policy, &codex_home);
let env_map = HashMap::new();
let roots = gather_read_roots(&command_cwd, &policy, &codex_home, &env_map);
let expected_writable =
dunce::canonicalize(&writable_root).expect("canonical writable root");
@@ -1105,7 +1187,8 @@ mod tests {
fs::create_dir_all(&command_cwd).expect("create workspace");
let policy = SandboxPolicy::new_read_only_policy();
let roots = gather_legacy_full_read_roots(&command_cwd, &policy, &codex_home);
let env_map = HashMap::new();
let roots = gather_legacy_full_read_roots(&command_cwd, &policy, &codex_home, &env_map);
assert!(
canonical_windows_platform_default_roots()