Preserve target-native paths in command approvals (#42577)

## What changed

- Pass the selected executor's `PathUri` through user command approval requests instead of converting the working directory to a host-native path.
- Make native-path permission helpers explicitly local and require executor-aware permission transforms to receive a `FileSystemSandboxPolicyContext`.
- Cover approved remote commands to verify that the approval request reports the selected remote working directory and execution resumes after approval.

GitOrigin-RevId: 0aa3042f2d7594af6c7d0abf31d7f99cf67d5556
This commit is contained in:
iceweasel-oai authored and copyberry committed 2026-09-03 16:21:10 +00:00
1 parent 0650d6d1ca
commit 7a7c188682
30 files changed
+304 -368

No files matched your search

+32 -28
View File
@@ -2383,7 +2383,7 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::
!config
.permissions
.file_system_sandbox_policy()
.can_write_path_with_cwd(&cwd.path().join(".git"), cwd.path())
.can_write_local_path_with_cwd(&cwd.path().join(".git"), cwd.path())
);
assert_eq!(
config.permissions.network_sandbox_policy(),
@@ -2828,7 +2828,7 @@ async fn permission_profile_override_keeps_memories_root_out_of_legacy_projectio
!config
.permissions
.file_system_sandbox_policy()
.can_write_path_with_cwd(memories_root.as_path(), cwd.path())
.can_write_local_path_with_cwd(memories_root.as_path(), cwd.path())
);
assert_eq!(
&config.legacy_sandbox_policy(),
@@ -3064,11 +3064,11 @@ async fn default_permissions_can_select_builtin_profile_without_permissions_tabl
Some(BUILT_IN_PERMISSION_PROFILE_WORKSPACE)
);
assert!(
policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected :workspace to allow writing the project root, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(&cwd.path().join(".git"), cwd.path()),
!policy.can_write_local_path_with_cwd(&cwd.path().join(".git"), cwd.path()),
"expected :workspace to protect project metadata, policy: {policy:?}"
);
Ok(())
@@ -3097,7 +3097,7 @@ async fn default_permissions_read_only_keeps_add_dir_read_only() -> std::io::Res
let policy = config.permissions.file_system_sandbox_policy();
assert!(
!policy.can_write_path_with_cwd(extra_root.as_path(), cwd.path()),
!policy.can_write_local_path_with_cwd(extra_root.as_path(), cwd.path()),
"expected :read-only to stay read-only for runtime workspace roots, policy: {policy:?}"
);
assert_eq!(
@@ -3186,15 +3186,15 @@ async fn workspace_profile_applies_rules_to_runtime_and_profile_workspace_roots(
let policy = config.permissions.file_system_sandbox_policy();
for root in [cwd_abs, runtime_root_abs, profile_root_abs.clone()] {
assert!(
policy.can_write_path_with_cwd(root.as_path(), cwd.as_path()),
policy.can_write_local_path_with_cwd(root.as_path(), cwd.as_path()),
"expected workspace root to be writable, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(&root.join(".git"), cwd.as_path()),
!policy.can_write_local_path_with_cwd(&root.join(".git"), cwd.as_path()),
"expected .git carveout under {root:?}, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(&root.join(".codex"), cwd.as_path()),
!policy.can_write_local_path_with_cwd(&root.join(".codex"), cwd.as_path()),
"expected .codex carveout under {root:?}, policy: {policy:?}"
);
}
@@ -3293,11 +3293,11 @@ async fn default_permissions_profile_can_extend_builtin_workspace() -> std::io::
let policy = config.permissions.file_system_sandbox_policy();
assert!(
policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected profile extending :workspace to keep project-root writes, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(&cwd.path().join(".git"), cwd.path()),
!policy.can_write_local_path_with_cwd(&cwd.path().join(".git"), cwd.path()),
"expected profile extending :workspace to keep metadata carveouts, policy: {policy:?}"
);
assert!(
@@ -3388,11 +3388,11 @@ async fn default_permissions_profile_can_extend_builtin_read_only() -> std::io::
let policy = config.permissions.file_system_sandbox_policy();
assert!(
policy.can_read_path_with_cwd(cwd.path(), cwd.path()),
policy.can_read_local_path_with_cwd(cwd.path(), cwd.path()),
"expected profile extending :read-only to keep read access, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
!policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected profile extending :read-only to stay non-writable, policy: {policy:?}"
);
assert_eq!(
@@ -3448,16 +3448,16 @@ async fn empty_config_defaults_to_builtin_profile_for_trusted_project() -> std::
);
if cfg!(target_os = "windows") {
assert!(
!policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
!policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected trusted project fallback to stay read-only without Windows sandbox support, policy: {policy:?}"
);
} else {
assert!(
policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected trusted project fallback to use :workspace, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(&cwd.path().join(".codex"), cwd.path()),
!policy.can_write_local_path_with_cwd(&cwd.path().join(".codex"), cwd.path()),
"expected :workspace metadata carveouts, policy: {policy:?}"
);
}
@@ -3502,21 +3502,21 @@ async fn empty_config_defaults_to_builtin_profile_for_untrusted_project() -> std
})
);
assert!(
policy.can_read_path_with_cwd(cwd.path(), cwd.path()),
policy.can_read_local_path_with_cwd(cwd.path(), cwd.path()),
"expected untrusted project fallback to allow reads, policy: {policy:?}"
);
if cfg!(target_os = "windows") {
assert!(
!policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
!policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected untrusted project fallback to stay read-only without Windows sandbox support, policy: {policy:?}"
);
} else {
assert!(
policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected untrusted project fallback to use :workspace, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(&cwd.path().join(".codex"), cwd.path()),
!policy.can_write_local_path_with_cwd(&cwd.path().join(".codex"), cwd.path()),
"expected :workspace metadata carveouts, policy: {policy:?}"
);
}
@@ -3562,7 +3562,7 @@ async fn implicit_builtin_workspace_profile_preserves_sandbox_workspace_write_se
let policy = config.permissions.file_system_sandbox_policy();
assert!(
policy.can_write_path_with_cwd(extra_root.as_path(), cwd.path()),
policy.can_write_local_path_with_cwd(extra_root.as_path(), cwd.path()),
"expected implicit :workspace to preserve sandbox_workspace_write.writable_roots, policy: {policy:?}"
);
assert_eq!(
@@ -3629,12 +3629,12 @@ async fn implicit_builtin_workspace_profile_preserves_add_dir_metadata_carveouts
let policy = config.permissions.file_system_sandbox_policy();
let extra_root = extra_root.path().abs();
assert!(
policy.can_write_path_with_cwd(extra_root.as_path(), cwd.path()),
policy.can_write_local_path_with_cwd(extra_root.as_path(), cwd.path()),
"expected implicit :workspace to preserve additional writable roots, policy: {policy:?}"
);
for subpath in [".git", ".agents", ".codex"] {
assert!(
!policy.can_write_path_with_cwd(&extra_root.join(subpath), cwd.path()),
!policy.can_write_local_path_with_cwd(&extra_root.join(subpath), cwd.path()),
"expected implicit :workspace to preserve legacy metadata carveout for {subpath}, \
policy: {policy:?}"
);
@@ -3660,11 +3660,11 @@ async fn empty_config_defaults_to_builtin_read_only_without_trust_decision() ->
let policy = config.permissions.file_system_sandbox_policy();
assert!(
policy.can_read_path_with_cwd(cwd.path(), cwd.path()),
policy.can_read_local_path_with_cwd(cwd.path(), cwd.path()),
"expected :read-only to allow reads, policy: {policy:?}"
);
assert!(
!policy.can_write_path_with_cwd(cwd.path(), cwd.path()),
!policy.can_write_local_path_with_cwd(cwd.path(), cwd.path()),
"expected :read-only to deny writes, policy: {policy:?}"
);
Ok(())
@@ -3842,7 +3842,7 @@ async fn permissions_profiles_allow_direct_write_roots_outside_workspace_root()
config
.permissions
.file_system_sandbox_policy()
.can_write_path_with_cwd(external_write_path.as_path(), cwd.path())
.can_write_local_path_with_cwd(external_write_path.as_path(), cwd.path())
);
assert_eq!(
&config.legacy_sandbox_policy(),
@@ -6035,8 +6035,12 @@ async fn memory_tool_makes_memories_root_readable_without_creating_or_widening_w
memories_root.display()
);
let file_system_policy = config.permissions.file_system_sandbox_policy();
assert!(file_system_policy.can_read_path_with_cwd(memories_root_abs.as_path(), cwd.path()));
assert!(!file_system_policy.can_write_path_with_cwd(memories_root_abs.as_path(), cwd.path()));
assert!(
file_system_policy.can_read_local_path_with_cwd(memories_root_abs.as_path(), cwd.path())
);
assert!(
!file_system_policy.can_write_local_path_with_cwd(memories_root_abs.as_path(), cwd.path())
);
if cfg!(target_os = "windows") {
match &config.legacy_sandbox_policy() {
@@ -10975,7 +10979,7 @@ async fn permission_profile_override_preserves_split_write_roots() -> std::io::R
config
.permissions
.file_system_sandbox_policy()
.can_write_path_with_cwd(outside_root.as_path(), config.cwd.as_path())
.can_write_local_path_with_cwd(outside_root.as_path(), config.cwd.as_path())
);
assert!(matches!(
&config.legacy_sandbox_policy(),
+3 -6
View File
@@ -4091,17 +4091,14 @@ impl Config {
}) = filesystem_requirements.as_ref()
&& let Some(managed_file_system_policy) = managed_deny_read_policy.as_ref()
{
let _initial_matcher =
ReadDenyMatcher::try_new(managed_file_system_policy, resolved_cwd.as_path())
let managed_deny_matcher =
ReadDenyMatcher::try_new_for_local_paths(managed_file_system_policy, resolved_cwd.as_path())
.map_err(std::io::Error::other)?;
let managed_file_system_policy = Arc::clone(managed_file_system_policy);
let permission_cwd = resolved_cwd.clone();
let requirement_source = requirement_source.clone();
constrained_permission_profile
.value
.add_validator(move |permission_profile| {
let managed_deny_matcher =
ReadDenyMatcher::new(&managed_file_system_policy, permission_cwd.as_path());
let file_system_policy = permission_profile.file_system_sandbox_policy();
let missing_required_deny = managed_file_system_policy
.entries
@@ -4118,7 +4115,7 @@ impl Config {
let path = path.to_abs_path().ok()?;
managed_deny_matcher
.as_ref()
.is_some_and(|matcher| matcher.is_read_denied(path.as_path()))
.is_some_and(|matcher| matcher.is_local_path_read_denied(path.as_path()))
.then_some(path)
});
if missing_required_deny || violating_root.is_some() {
@@ -96,15 +96,15 @@ async fn restricted_read_implicitly_allows_helper_executables() -> std::io::Resu
let policy = config.permissions.file_system_sandbox_policy();
assert!(
policy.can_read_path_with_cwd(expected_zsh.as_path(), &cwd),
policy.can_read_local_path_with_cwd(expected_zsh.as_path(), &cwd),
"expected zsh helper path to be readable, policy: {policy:?}"
);
assert!(
policy.can_read_path_with_cwd(expected_allowed_arg0_dir.as_path(), &cwd),
policy.can_read_local_path_with_cwd(expected_allowed_arg0_dir.as_path(), &cwd),
"expected active arg0 helper dir to be readable, policy: {policy:?}"
);
assert!(
!policy.can_read_path_with_cwd(expected_sibling_arg0_dir.as_path(), &cwd),
!policy.can_read_local_path_with_cwd(expected_sibling_arg0_dir.as_path(), &cwd),
"expected sibling arg0 helper dir to remain unreadable, policy: {policy:?}"
);
@@ -493,7 +493,7 @@ docs = "read"
let (read_deny_policy, _) =
compile_permission_profile(&permissions, "read_deny", &mut startup_warnings)?;
assert_eq!(
read_deny_policy.resolve_access_with_cwd(cwd.path(), cwd.path()),
read_deny_policy.resolve_access_for_local_path_with_cwd(cwd.path(), cwd.path()),
FileSystemAccessMode::Deny
);
@@ -501,7 +501,7 @@ docs = "read"
compile_permission_profile(&permissions, "write_deny", &mut startup_warnings)?;
assert!(!write_deny_policy.has_full_disk_write_access());
assert_eq!(
write_deny_policy.resolve_access_with_cwd(cwd.path(), cwd.path()),
write_deny_policy.resolve_access_for_local_path_with_cwd(cwd.path(), cwd.path()),
FileSystemAccessMode::Deny
);
@@ -509,7 +509,7 @@ docs = "read"
compile_permission_profile(&permissions, "write_read", &mut startup_warnings)?;
assert!(!write_read_policy.has_full_disk_write_access());
assert_eq!(
write_read_policy.resolve_access_with_cwd(&docs, cwd.path()),
write_read_policy.resolve_access_for_local_path_with_cwd(&docs, cwd.path()),
FileSystemAccessMode::Read
);