diff --git a/codex-rs/core/src/codex_tests_guardian.rs b/codex-rs/core/src/codex_tests_guardian.rs index 400b987bae..0713de8365 100644 --- a/codex-rs/core/src/codex_tests_guardian.rs +++ b/codex-rs/core/src/codex_tests_guardian.rs @@ -2,24 +2,21 @@ use super::*; use crate::config_loader::ConfigLayerEntry; use crate::config_loader::ConfigRequirements; use crate::config_loader::ConfigRequirementsToml; -use crate::exec::ExecParams; use crate::exec_policy::ExecPolicyManager; use crate::features::Feature; use crate::guardian::GUARDIAN_SUBAGENT_NAME; use crate::protocol::AskForApproval; use crate::sandboxing::SandboxPermissions; +use crate::tools::handlers::normalize_and_validate_additional_permissions; use crate::turn_diff_tracker::TurnDiffTracker; use codex_app_server_protocol::ConfigLayerSource; use codex_execpolicy::Decision; use codex_execpolicy::Evaluation; use codex_execpolicy::RuleMatch; -use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::NetworkPermissions; use codex_protocol::models::PermissionProfile; use codex_utils_absolute_path::AbsolutePathBuf; use pretty_assertions::assert_eq; -use serde::Deserialize; -use std::collections::HashMap; use std::fs; use std::sync::Arc; use tempfile::tempdir; @@ -39,93 +36,23 @@ async fn guardian_allows_shell_additional_permissions_requests_past_policy_valid .features .enable(Feature::RequestPermissions) .expect("test setup should allow enabling request permissions"); - turn_context_raw - .sandbox_policy - .set(SandboxPolicy::DangerFullAccess) - .expect("test setup should allow updating sandbox policy"); - turn_context_raw.file_system_sandbox_policy = - FileSystemSandboxPolicy::from(turn_context_raw.sandbox_policy.get()); - turn_context_raw.network_sandbox_policy = - NetworkSandboxPolicy::from(turn_context_raw.sandbox_policy.get()); - let session = Arc::new(session); - let turn_context = Arc::new(turn_context_raw); - - let params = ExecParams { - command: if cfg!(windows) { - vec![ - "cmd.exe".to_string(), - "/C".to_string(), - "echo hi".to_string(), - ] - } else { - vec![ - "/bin/sh".to_string(), - "-c".to_string(), - "echo hi".to_string(), - ] - }, - cwd: turn_context.cwd.clone(), - expiration: 1000.into(), - env: HashMap::new(), - network: None, - sandbox_permissions: SandboxPermissions::WithAdditionalPermissions, - windows_sandbox_level: turn_context.windows_sandbox_level, - justification: Some("test".to_string()), - arg0: None, + let additional_permissions = PermissionProfile { + network: Some(NetworkPermissions { + enabled: Some(true), + }), + file_system: None, + macos: None, }; + let normalized = normalize_and_validate_additional_permissions( + session.features().enabled(Feature::RequestPermissions), + turn_context_raw.approval_policy.value(), + SandboxPermissions::WithAdditionalPermissions, + Some(additional_permissions.clone()), + &turn_context_raw.cwd, + ) + .expect("shell additional permissions should pass policy validation"); - let handler = ShellHandler; - let resp = handler - .handle(ToolInvocation { - session: Arc::clone(&session), - turn: Arc::clone(&turn_context), - tracker: Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::new())), - call_id: "test-call".to_string(), - tool_name: "shell".to_string(), - payload: ToolPayload::Function { - arguments: serde_json::json!({ - "command": params.command.clone(), - "workdir": Some(turn_context.cwd.to_string_lossy().to_string()), - "timeout_ms": params.expiration.timeout_ms(), - "sandbox_permissions": params.sandbox_permissions, - "additional_permissions": PermissionProfile { - network: Some(NetworkPermissions { - enabled: Some(true), - }), - file_system: None, - macos: None, - }, - "justification": params.justification.clone(), - }) - .to_string(), - }, - }) - .await; - - let output = match resp.expect("expected Ok result") { - ToolOutput::Function { - body: FunctionCallOutputBody::Text(content), - .. - } => content, - _ => panic!("unexpected tool output"), - }; - - #[derive(Deserialize, PartialEq, Eq, Debug)] - struct ResponseExecMetadata { - exit_code: i32, - } - - #[derive(Deserialize)] - struct ResponseExecOutput { - output: String, - metadata: ResponseExecMetadata, - } - - let exec_output: ResponseExecOutput = - serde_json::from_str(&output).expect("valid exec output json"); - - assert_eq!(exec_output.metadata, ResponseExecMetadata { exit_code: 0 }); - assert!(exec_output.output.contains("hi")); + assert_eq!(normalized, Some(additional_permissions)); } #[tokio::test] diff --git a/codex-rs/core/src/tools/handlers/mod.rs b/codex-rs/core/src/tools/handlers/mod.rs index 086701d9c2..a473ce6fbc 100644 --- a/codex-rs/core/src/tools/handlers/mod.rs +++ b/codex-rs/core/src/tools/handlers/mod.rs @@ -90,7 +90,7 @@ fn resolve_workdir_base_path( /// Validates feature/policy constraints for `with_additional_permissions` and /// normalizes any path-based permissions. Errors if the request is invalid. -pub(super) fn normalize_and_validate_additional_permissions( +pub(crate) fn normalize_and_validate_additional_permissions( request_permission_enabled: bool, approval_policy: AskForApproval, sandbox_permissions: SandboxPermissions,