Preserve foreign paths in managed network approvals (#39040)

## Why

Unified exec could drop network approval registration when a selected environment used a Windows working directory on a non-Windows host because the trigger path could not be converted to a host absolute path.

## What changed

- Keep network approval trigger working directories as `PathUri` values and render them with their inferred native path syntax for Guardian requests.
- Fall back to the selected turn environment only when a trigger path can be converted to a host absolute path.
- Reject selected environment working directories larger than 8 KiB at the thread-manager validation boundary.

## Testing

- Cover managed-network approval registration for a foreign Windows working directory.
- Verify that oversized working directories are rejected as invalid `thread/start` requests.

GitOrigin-RevId: 646fbf12115a35a7e5cb04b511464797bee6647b
This commit is contained in:
iceweasel-oai
2026-08-17 18:15:56 +00:00
committed by copyberry
parent 9dd3d6a13e
commit d0fd4e830a
9 changed files with 78 additions and 20 deletions
+5 -2
View File
@@ -699,8 +699,11 @@ impl NetworkApprovalService {
let command = owner_call
.as_ref()
.map_or_else(|| prompt_command.join(" "), |call| call.command.clone());
let cwd = if let Some(owner_call) = owner_call.as_ref() {
owner_call.trigger.cwd.clone()
let cwd = if let Some(cwd) = owner_call
.as_ref()
.and_then(|owner_call| owner_call.trigger.cwd.to_abs_path().ok())
{
cwd
} else {
turn_context
.environments
@@ -4,6 +4,7 @@ use codex_network_proxy::BlockedRequestArgs;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_utils_path_uri::PathUri;
use core_test_support::PathBufExt;
use core_test_support::test_path_buf;
use futures::poll;
@@ -481,7 +482,7 @@ async fn register_call_with_default_shell_trigger(
call_id: "call-1".to_string(),
tool_name: "shell_command".to_string(),
command: vec!["curl".to_string(), "https://example.com".to_string()],
cwd: test_path_buf("/tmp").abs(),
cwd: PathUri::from_abs_path(&test_path_buf("/tmp").abs()),
sandbox_permissions: SandboxPermissions::UseDefault,
additional_permissions: None,
justification: None,
@@ -503,7 +504,7 @@ async fn active_call_preserves_triggering_command_context() {
call_id: "call-1".to_string(),
tool_name: "shell_command".to_string(),
command: vec!["curl".to_string(), "https://example.com".to_string()],
cwd: test_path_buf("/repo").abs(),
cwd: PathUri::parse("file:///C:/repo").expect("valid Windows path URI"),
sandbox_permissions: SandboxPermissions::UseDefault,
additional_permissions: None,
justification: Some("fetch release metadata".to_string()),
+1 -1
View File
@@ -179,7 +179,7 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
call_id: ctx.call_id.clone(),
tool_name: flat_tool_name(&ctx.tool_name).into_owned(),
command: req.command.clone(),
cwd: req.cwd.clone(),
cwd: PathUri::from_abs_path(&req.cwd),
sandbox_permissions: req.sandbox_permissions,
additional_permissions: req.additional_permissions.clone(),
justification: req.justification.clone(),
@@ -230,7 +230,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecAttempt> for UnifiedExecRunt
call_id: ctx.call_id.clone(),
tool_name: flat_tool_name(&ctx.tool_name).into_owned(),
command: req.command.clone(),
cwd: req.cwd.to_abs_path().ok()?,
cwd: req.cwd.clone(),
sandbox_permissions: req.sandbox_permissions,
additional_permissions: req.additional_permissions.clone(),
justification: req.justification.clone(),