fix Windows deny-read for shell runtimes

This commit is contained in:
Abhinav Vedmala
2026-06-08 09:07:42 -07:00
parent 5a440c03f2
commit d4fa4853be
6 changed files with 107 additions and 10 deletions
+9 -3
View File
@@ -385,7 +385,7 @@ pub fn build_exec_request(
expiration,
capture_policy,
};
let mut exec_req = manager
let exec_req = manager
.transform(SandboxTransformRequest {
command,
permissions: permission_profile,
@@ -414,6 +414,12 @@ pub fn build_exec_request(
)
})
.map_err(CodexErr::from)?;
apply_windows_sandbox_filesystem_overrides(exec_req)
}
pub(crate) fn apply_windows_sandbox_filesystem_overrides(
mut exec_req: ExecRequest,
) -> Result<ExecRequest> {
let use_windows_elevated_backend = windows_sandbox_uses_elevated_backend(
exec_req.windows_sandbox_level,
exec_req.network.is_some(),
@@ -422,14 +428,14 @@ pub fn build_exec_request(
resolve_windows_elevated_filesystem_overrides(
exec_req.sandbox,
&exec_req.permission_profile,
sandbox_cwd,
&exec_req.windows_sandbox_policy_cwd,
use_windows_elevated_backend,
)
} else {
resolve_windows_restricted_token_filesystem_overrides(
exec_req.sandbox,
&exec_req.permission_profile,
sandbox_cwd,
&exec_req.windows_sandbox_policy_cwd,
exec_req.windows_sandbox_level,
)
}
+1 -1
View File
@@ -310,7 +310,7 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
};
let env = attempt
.env_for(command, options, managed_network)
.map_err(|err| ToolError::Codex(err.into()))?;
.map_err(ToolError::Codex)?;
let out = execute_env(env, Self::stdout_stream(ctx))
.await
.map_err(ToolError::Codex)?;
@@ -141,7 +141,7 @@ pub(super) async fn try_run_zsh_fork(
options,
managed_network_for_sandbox_permissions(req.network.as_ref(), req.sandbox_permissions),
)
.map_err(|err| ToolError::Codex(err.into()))?;
.map_err(ToolError::Codex)?;
let crate::sandboxing::ExecRequest {
command,
cwd: sandbox_cwd,
@@ -330,7 +330,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
let options = unified_exec_options(attempt.network_denial_cancellation_token.clone());
let mut exec_env = attempt
.env_for(command, options, managed_network)
.map_err(|err| ToolError::Codex(err.into()))?;
.map_err(ToolError::Codex)?;
exec_env.exec_server_env_config = req.exec_server_env_config.clone();
match zsh_fork_backend::maybe_prepare_unified_exec(
req,
@@ -381,7 +381,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
let options = unified_exec_options(attempt.network_denial_cancellation_token.clone());
let mut exec_env = attempt
.env_for(command, options, managed_network)
.map_err(|err| ToolError::Codex(err.into()))?;
.map_err(ToolError::Codex)?;
exec_env.exec_server_env_config = req.exec_server_env_config.clone();
self.manager
.open_session_with_exec_env(
+5 -3
View File
@@ -21,7 +21,6 @@ use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxTransformError;
use codex_sandboxing::SandboxTransformRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::SandboxablePreference;
@@ -426,8 +425,9 @@ impl<'a> SandboxAttempt<'a> {
command: SandboxCommand,
options: ExecOptions,
network: Option<&NetworkProxy>,
) -> Result<crate::sandboxing::ExecRequest, SandboxTransformError> {
self.manager
) -> codex_protocol::error::Result<crate::sandboxing::ExecRequest> {
let exec_request = self
.manager
.transform(SandboxTransformRequest {
command,
permissions: self.permissions,
@@ -455,6 +455,8 @@ impl<'a> SandboxAttempt<'a> {
self.workspace_roots.to_vec(),
)
})
.map_err(CodexErr::from)?;
crate::exec::apply_windows_sandbox_filesystem_overrides(exec_request)
}
}
@@ -1,12 +1,101 @@
use super::*;
use crate::exec::ExecCapturePolicy;
use crate::exec::ExecExpiration;
use crate::exec::WindowsSandboxFilesystemOverrides;
use crate::sandboxing::ExecOptions;
use crate::sandboxing::SandboxPermissions;
use crate::tools::hook_names::HookToolName;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSpecialPath;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::GranularApprovalConfig;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxType;
use codex_utils_absolute_path::test_support::PathBufExt;
use codex_utils_absolute_path::test_support::PathExt;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::collections::HashMap;
#[test]
fn shell_sandbox_attempt_applies_windows_deny_read_overrides() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
let blocked = temp_dir.path().join(".env");
std::fs::write(&blocked, "secret").expect("write blocked file");
let expected_blocked = dunce::canonicalize(&blocked)
.expect("canonical blocked file")
.abs();
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: blocked.abs(),
},
access: FileSystemAccessMode::Deny,
},
]);
let permissions = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
let sandbox_cwd = temp_dir.path().abs();
let manager = SandboxManager::new();
let attempt = SandboxAttempt {
sandbox: SandboxType::WindowsRestrictedToken,
permissions: &permissions,
enforce_managed_network: false,
manager: &manager,
sandbox_cwd: &sandbox_cwd,
workspace_roots: std::slice::from_ref(&sandbox_cwd),
codex_linux_sandbox_exe: None,
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Elevated,
windows_sandbox_private_desktop: false,
network_denial_cancellation_token: None,
};
let command = SandboxCommand {
program: "echo".into(),
args: vec!["ok".to_string()],
cwd: sandbox_cwd.clone(),
env: HashMap::new(),
additional_permissions: None,
};
let options = ExecOptions {
expiration: ExecExpiration::DefaultTimeout,
capture_policy: ExecCapturePolicy::ShellTool,
};
let exec_request = attempt
.env_for(command, options, /*network*/ None)
.expect("prepare shell exec request");
assert_eq!(
exec_request.windows_sandbox_filesystem_overrides,
Some(WindowsSandboxFilesystemOverrides {
read_roots_override: None,
read_roots_include_platform_defaults: false,
write_roots_override: None,
additional_deny_read_paths: vec![expected_blocked.clone()],
additional_deny_write_paths: vec![expected_blocked],
})
);
}
#[test]
fn bash_permission_request_payload_omits_missing_description() {