Download codex-rs/core/src/tools/handlers/mod.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 23.6 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/tools/handlers/mod.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/core/src/tools/handlers/mod.rs
-
curl -L -o mod.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/tools/handlers/mod.rs
23.6 kB
| pub(crate) mod apply_patch; | |
| pub(crate) mod apply_patch_spec; | |
| mod current_time; | |
| mod dynamic; | |
| pub(crate) mod extension_tools; | |
| mod get_context_remaining; | |
| pub(crate) mod get_context_remaining_spec; | |
| mod list_available_plugins_to_install; | |
| pub(crate) mod list_available_plugins_to_install_spec; | |
| mod mcp; | |
| mod mcp_resource; | |
| pub(crate) mod mcp_resource_spec; | |
| pub(crate) mod multi_agents; | |
| pub(crate) mod multi_agents_common; | |
| pub(crate) mod multi_agents_spec; | |
| pub(crate) mod multi_agents_v2; | |
| mod new_context_window; | |
| pub(crate) mod new_context_window_spec; | |
| mod plan; | |
| pub(crate) mod plan_spec; | |
| mod request_permissions; | |
| mod request_plugin_install; | |
| pub(crate) mod request_plugin_install_spec; | |
| mod request_user_input; | |
| mod request_user_input_async; | |
| pub(crate) mod request_user_input_spec; | |
| mod send_message_to_user_async; | |
| pub(crate) mod shell_spec; | |
| mod sleep; | |
| mod test_sync; | |
| pub(crate) mod test_sync_spec; | |
| mod tool_search; | |
| pub(crate) mod tool_search_spec; | |
| pub(crate) mod unified_exec; | |
| mod view_image; | |
| pub(crate) mod view_image_spec; | |
| mod wait_for_environment; | |
| use codex_file_system::FileSystemSandboxContext; | |
| use codex_sandboxing::policy_transforms::materialize_additional_permissions_with_context; | |
| use codex_sandboxing::policy_transforms::merge_permission_profiles; | |
| use codex_sandboxing::policy_transforms::normalize_additional_permissions_with_context; | |
| use codex_utils_absolute_path::AbsolutePathBuf; | |
| use codex_utils_absolute_path::AbsolutePathBufGuard; | |
| use codex_utils_path_uri::PathUri; | |
| use serde::Deserialize; | |
| use serde_json::Map; | |
| use serde_json::Value; | |
| use crate::environment_selection::TurnEnvironmentSnapshot; | |
| use crate::function_tool::FunctionCallError; | |
| use crate::sandboxing::SandboxPermissions; | |
| use crate::session::session::Session; | |
| use crate::session::turn_context::TurnEnvironment; | |
| pub(crate) use crate::tools::code_mode::CodeModeExecuteHandler; | |
| pub(crate) use crate::tools::code_mode::CodeModeWaitHandler; | |
| pub use apply_patch::ApplyPatchHandler; | |
| use codex_protocol::models::AdditionalPermissionProfile; | |
| use codex_protocol::protocol::AskForApproval; | |
| pub use current_time::CurrentTimeHandler; | |
| pub use dynamic::DynamicToolHandler; | |
| pub use get_context_remaining::GetContextRemainingHandler; | |
| pub use list_available_plugins_to_install::ListAvailablePluginsToInstallHandler; | |
| pub use mcp::McpHandler; | |
| pub use mcp_resource::ListMcpResourceTemplatesHandler; | |
| pub use mcp_resource::ListMcpResourcesHandler; | |
| pub use mcp_resource::ReadMcpResourceHandler; | |
| pub use new_context_window::NewContextWindowHandler; | |
| pub use plan::PlanHandler; | |
| pub use request_permissions::RequestPermissionsHandler; | |
| pub use request_plugin_install::RequestPluginInstallHandler; | |
| pub use request_user_input::RequestUserInputHandler; | |
| pub use request_user_input_async::RequestUserInputAsyncHandler; | |
| pub use send_message_to_user_async::SendMessageToUserAsyncHandler; | |
| pub use sleep::SleepHandler; | |
| pub use test_sync::TestSyncHandler; | |
| pub(crate) use tool_search::ToolSearchHandlerCache; | |
| pub use unified_exec::ExecCommandHandler; | |
| pub(crate) use unified_exec::ExecCommandHandlerOptions; | |
| pub use unified_exec::WriteStdinHandler; | |
| pub use view_image::ViewImageHandler; | |
| pub(crate) use wait_for_environment::WaitForEnvironmentHandler; | |
| pub use wait_for_environment::WaitForEnvironmentToolConfig; | |
| pub(crate) fn parse_arguments<T>(arguments: &str) -> Result<T, FunctionCallError> | |
| where | |
| T: for<'de> Deserialize<'de>, | |
| { | |
| serde_json::from_str(arguments).map_err(|err| { | |
| FunctionCallError::RespondToModel(format!("failed to parse function arguments: {err}")) | |
| }) | |
| } | |
| fn resolve_sandbox_permissions( | |
| sandbox_permissions: Option<SandboxPermissions>, | |
| justification: Option<&str>, | |
| ) -> Result<SandboxPermissions, FunctionCallError> { | |
| if justification.is_some() && sandbox_permissions.is_none() { | |
| return Err(FunctionCallError::RespondToModel( | |
| "`justification` requires an explicit `sandbox_permissions`; use `sandbox_permissions: \"require_escalated\"` for unsandboxed execution, or omit `justification`.".to_string(), | |
| )); | |
| } | |
| Ok(sandbox_permissions.unwrap_or_default()) | |
| } | |
| fn updated_hook_command(updated_input: &Value) -> Result<&str, FunctionCallError> { | |
| updated_input | |
| .get("command") | |
| .and_then(Value::as_str) | |
| .ok_or_else(|| { | |
| FunctionCallError::RespondToModel( | |
| "hook returned updatedInput without string field `command`".to_string(), | |
| ) | |
| }) | |
| } | |
| fn rewrite_function_arguments( | |
| arguments: &str, | |
| tool_name: &str, | |
| rewrite: impl FnOnce(&mut Map<String, Value>), | |
| ) -> Result<String, FunctionCallError> { | |
| let mut arguments: Value = parse_arguments(arguments)?; | |
| let Value::Object(arguments) = &mut arguments else { | |
| return Err(FunctionCallError::RespondToModel(format!( | |
| "{tool_name} arguments must be an object" | |
| ))); | |
| }; | |
| rewrite(arguments); | |
| serde_json::to_string(&arguments).map_err(|err| { | |
| FunctionCallError::RespondToModel(format!( | |
| "failed to serialize rewritten {tool_name} arguments: {err}" | |
| )) | |
| }) | |
| } | |
| fn rewrite_function_string_argument( | |
| arguments: &str, | |
| tool_name: &str, | |
| field_name: &str, | |
| value: &str, | |
| ) -> Result<String, FunctionCallError> { | |
| rewrite_function_arguments(arguments, tool_name, |arguments| { | |
| arguments.insert(field_name.to_string(), Value::String(value.to_string())); | |
| }) | |
| } | |
| fn parse_arguments_with_base_path<T>( | |
| arguments: &str, | |
| base_path: &AbsolutePathBuf, | |
| ) -> Result<T, FunctionCallError> | |
| where | |
| T: for<'de> Deserialize<'de>, | |
| { | |
| let _guard = AbsolutePathBufGuard::new(base_path); | |
| parse_arguments(arguments) | |
| } | |
| fn resolve_tool_environment<'a>( | |
| environments: &'a TurnEnvironmentSnapshot, | |
| environment_id: Option<&str>, | |
| ) -> Result<Option<&'a TurnEnvironment>, FunctionCallError> { | |
| environment_id.map_or_else( | |
| || Ok(environments.primary()), | |
| |environment_id| { | |
| environments | |
| .turn_environments() | |
| .find(|environment| environment.selection.environment_id == environment_id) | |
| .map(Some) | |
| .ok_or_else(|| { | |
| FunctionCallError::RespondToModel(format!( | |
| "unknown turn environment id `{environment_id}`" | |
| )) | |
| }) | |
| }, | |
| ) | |
| } | |
| /// Validates feature/policy constraints for `with_additional_permissions` and | |
| /// normalizes any path-based permissions. Errors if the request is invalid. | |
| pub(crate) fn normalize_and_validate_additional_permissions( | |
| additional_permissions_allowed: bool, | |
| approval_policy: AskForApproval, | |
| sandbox_permissions: SandboxPermissions, | |
| additional_permissions: Option<AdditionalPermissionProfile>, | |
| permissions_preapproved: bool, | |
| context: &codex_protocol::permissions::FileSystemSandboxPolicyContext<'_>, | |
| ) -> Result<Option<AdditionalPermissionProfile>, String> { | |
| let uses_additional_permissions = matches!( | |
| sandbox_permissions, | |
| SandboxPermissions::WithAdditionalPermissions | |
| ); | |
| if !permissions_preapproved | |
| && !additional_permissions_allowed | |
| && (uses_additional_permissions || additional_permissions.is_some()) | |
| { | |
| return Err( | |
| "additional permissions are disabled; enable `features.exec_permission_approvals` before using `with_additional_permissions`" | |
| .to_string(), | |
| ); | |
| } | |
| if uses_additional_permissions { | |
| if !permissions_preapproved && !matches!(approval_policy, AskForApproval::OnRequest) { | |
| return Err(format!( | |
| "approval policy is {approval_policy:?}; reject command — you cannot request additional permissions unless the approval policy is OnRequest" | |
| )); | |
| } | |
| let Some(additional_permissions) = additional_permissions else { | |
| return Err( | |
| "missing `additional_permissions`; provide at least one of `network` or `file_system` when using `with_additional_permissions`" | |
| .to_string(), | |
| ); | |
| }; | |
| let normalized = | |
| normalize_additional_permissions_with_context(additional_permissions, context)?; | |
| if normalized.is_empty() { | |
| return Err( | |
| "`additional_permissions` must include at least one requested permission in `network` or `file_system`" | |
| .to_string(), | |
| ); | |
| } | |
| return Ok(Some(normalized)); | |
| } | |
| if additional_permissions.is_some() { | |
| Err( | |
| "`additional_permissions` requires `sandbox_permissions` set to `with_additional_permissions`" | |
| .to_string(), | |
| ) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| pub(super) struct EffectiveAdditionalPermissions { | |
| pub sandbox_permissions: SandboxPermissions, | |
| pub additional_permissions: Option<AdditionalPermissionProfile>, | |
| pub permissions_preapproved: bool, | |
| } | |
| pub(super) fn file_system_sandbox_policy_context_for_cwd<'a>( | |
| sandbox_context: &'a FileSystemSandboxContext, | |
| cwd: &'a PathUri, | |
| ) -> Option<codex_protocol::permissions::FileSystemSandboxPolicyContext<'a>> { | |
| let mut context = sandbox_context.policy_context()?; | |
| context.cwd = cwd; | |
| Some(context) | |
| } | |
| pub(super) fn implicit_granted_permissions( | |
| sandbox_permissions: SandboxPermissions, | |
| additional_permissions: Option<&AdditionalPermissionProfile>, | |
| effective_additional_permissions: &EffectiveAdditionalPermissions, | |
| ) -> Option<AdditionalPermissionProfile> { | |
| if !sandbox_permissions.uses_additional_permissions() | |
| && !matches!(sandbox_permissions, SandboxPermissions::RequireEscalated) | |
| && additional_permissions.is_none() | |
| { | |
| effective_additional_permissions | |
| .additional_permissions | |
| .clone() | |
| } else { | |
| None | |
| } | |
| } | |
| pub(super) async fn apply_granted_turn_permissions( | |
| session: &Session, | |
| environment: &TurnEnvironment, | |
| cwd: &PathUri, | |
| sandbox_permissions: SandboxPermissions, | |
| additional_permissions: Option<AdditionalPermissionProfile>, | |
| ) -> EffectiveAdditionalPermissions { | |
| if matches!(sandbox_permissions, SandboxPermissions::RequireEscalated) { | |
| return EffectiveAdditionalPermissions { | |
| sandbox_permissions, | |
| additional_permissions, | |
| permissions_preapproved: false, | |
| }; | |
| } | |
| let environment_id = &environment.selection.environment_id; | |
| let granted_session_permissions = session.granted_session_permissions(environment_id).await; | |
| let granted_turn_permissions = session.granted_turn_permissions(environment_id).await; | |
| let granted_permissions = merge_permission_profiles( | |
| granted_session_permissions.as_ref(), | |
| granted_turn_permissions.as_ref(), | |
| ); | |
| let effective_permissions = merge_permission_profiles( | |
| additional_permissions.as_ref(), | |
| granted_permissions.as_ref(), | |
| ); | |
| let sandbox_context = environment.sandbox_context(/*additional_permissions*/ None); | |
| let context = file_system_sandbox_policy_context_for_cwd(&sandbox_context, cwd); | |
| let preapproved_permissions = granted_permissions.as_ref().and_then(|granted| { | |
| if additional_permissions.is_none() { | |
| Some(granted.clone()) | |
| } else { | |
| effective_permissions.as_ref().and_then(|effective| { | |
| preapproved_permission_profile(effective, granted, context.as_ref()?) | |
| }) | |
| } | |
| }); | |
| let permissions_preapproved = preapproved_permissions.is_some(); | |
| // A preapproved command must execute with the stored authority, never an | |
| // unchecked merge that could reopen one of the grant's denied paths. | |
| let effective_permissions = preapproved_permissions.or(effective_permissions); | |
| let sandbox_permissions = | |
| if effective_permissions.is_some() && !sandbox_permissions.uses_additional_permissions() { | |
| SandboxPermissions::WithAdditionalPermissions | |
| } else { | |
| sandbox_permissions | |
| }; | |
| EffectiveAdditionalPermissions { | |
| sandbox_permissions, | |
| additional_permissions: effective_permissions, | |
| permissions_preapproved, | |
| } | |
| } | |
| fn preapproved_permission_profile( | |
| effective_permissions: &AdditionalPermissionProfile, | |
| granted_permissions: &AdditionalPermissionProfile, | |
| context: &codex_protocol::permissions::FileSystemSandboxPolicyContext<'_>, | |
| ) -> Option<AdditionalPermissionProfile> { | |
| if effective_permissions | |
| .file_system | |
| .as_ref() | |
| .is_some_and(|permissions| { | |
| permissions.entries.iter().any(|entry| { | |
| (matches!( | |
| &entry.path, | |
| codex_protocol::permissions::FileSystemPath::Special { | |
| value: codex_protocol::permissions::FileSystemSpecialPath::Tmpdir, | |
| } | |
| ) && context | |
| .temporary_directories | |
| .is_none_or(<[PathUri]>::is_empty)) | |
| || matches!( | |
| &entry.path, | |
| codex_protocol::permissions::FileSystemPath::Special { | |
| value: codex_protocol::permissions::FileSystemSpecialPath::ProjectRoots { .. }, | |
| } if context.workspace_roots.is_empty() | |
| ) | |
| }) | |
| }) | |
| { | |
| return None; | |
| } | |
| let (Ok(effective), Ok(granted)) = ( | |
| materialize_additional_permissions_with_context(effective_permissions.clone(), context), | |
| materialize_additional_permissions_with_context(granted_permissions.clone(), context), | |
| ) else { | |
| return None; | |
| }; | |
| if effective.network != granted.network { | |
| return None; | |
| } | |
| let unchanged = match (effective.file_system, granted.file_system) { | |
| (Some(effective), Some(granted)) => { | |
| effective.glob_scan_max_depth == granted.glob_scan_max_depth | |
| && effective.entries.len() == granted.entries.len() | |
| && effective | |
| .entries | |
| .iter() | |
| .all(|entry| granted.entries.contains(entry)) | |
| } | |
| (None, None) => true, | |
| (Some(_), None) | (None, Some(_)) => false, | |
| }; | |
| unchanged.then(|| granted_permissions.clone()) | |
| } | |
| mod permission_preapproval_tests; | |
| mod tests { | |
| use super::EffectiveAdditionalPermissions; | |
| use super::implicit_granted_permissions; | |
| use super::normalize_and_validate_additional_permissions; | |
| use super::preapproved_permission_profile; | |
| use crate::sandboxing::SandboxPermissions; | |
| use codex_protocol::models::AdditionalPermissionProfile; | |
| use codex_protocol::models::FileSystemPermissions; | |
| use codex_protocol::models::NetworkPermissions; | |
| use codex_protocol::permissions::FileSystemAccessMode; | |
| use codex_protocol::permissions::FileSystemPath; | |
| use codex_protocol::permissions::FileSystemSandboxEntry; | |
| use codex_protocol::permissions::FileSystemSandboxPolicyContext; | |
| use codex_protocol::permissions::FileSystemSpecialPath; | |
| use codex_protocol::protocol::AskForApproval; | |
| use codex_protocol::protocol::GranularApprovalConfig; | |
| use codex_sandboxing::policy_transforms::intersect_permission_profiles_with_context; | |
| use codex_sandboxing::policy_transforms::merge_permission_profiles; | |
| use codex_utils_absolute_path::AbsolutePathBuf; | |
| use codex_utils_path_uri::PathUri; | |
| use pretty_assertions::assert_eq; | |
| use tempfile::tempdir; | |
| fn network_permissions() -> AdditionalPermissionProfile { | |
| AdditionalPermissionProfile { | |
| network: Some(NetworkPermissions { | |
| enabled: Some(true), | |
| }), | |
| ..Default::default() | |
| } | |
| } | |
| fn file_system_permissions(path: &std::path::Path) -> AdditionalPermissionProfile { | |
| AdditionalPermissionProfile { | |
| file_system: Some(FileSystemPermissions::from_read_write_roots( | |
| /*read*/ None, | |
| Some(vec![ | |
| AbsolutePathBuf::from_absolute_path(path).expect("absolute path"), | |
| ]), | |
| )), | |
| ..Default::default() | |
| } | |
| } | |
| fn local_context(cwd: &PathUri) -> FileSystemSandboxPolicyContext<'_> { | |
| FileSystemSandboxPolicyContext { | |
| cwd, | |
| workspace_roots: std::slice::from_ref(cwd), | |
| temporary_directories: None, | |
| user_home_dir: None, | |
| } | |
| } | |
| fn preapproved_permissions_work_when_request_permissions_tool_is_enabled_without_exec_permission_approvals_feature() | |
| { | |
| let cwd = tempdir().expect("tempdir"); | |
| let cwd = PathUri::from_host_native_path(cwd.path()).expect("cwd URI"); | |
| let normalized = normalize_and_validate_additional_permissions( | |
| /*additional_permissions_allowed*/ false, | |
| AskForApproval::Granular(GranularApprovalConfig { | |
| sandbox_approval: true, | |
| rules: true, | |
| skill_approval: true, | |
| request_permissions: false, | |
| mcp_elicitations: true, | |
| }), | |
| SandboxPermissions::WithAdditionalPermissions, | |
| Some(network_permissions()), | |
| /*permissions_preapproved*/ true, | |
| &local_context(&cwd), | |
| ) | |
| .expect("preapproved permissions should be allowed"); | |
| assert_eq!(normalized, Some(network_permissions())); | |
| } | |
| fn fresh_additional_permissions_still_require_exec_permission_approvals_feature() { | |
| let cwd = tempdir().expect("tempdir"); | |
| let cwd = PathUri::from_host_native_path(cwd.path()).expect("cwd URI"); | |
| let err = normalize_and_validate_additional_permissions( | |
| /*additional_permissions_allowed*/ false, | |
| AskForApproval::OnRequest, | |
| SandboxPermissions::WithAdditionalPermissions, | |
| Some(network_permissions()), | |
| /*permissions_preapproved*/ false, | |
| &local_context(&cwd), | |
| ) | |
| .expect_err("fresh inline permission requests should remain disabled"); | |
| assert_eq!( | |
| err, | |
| "additional permissions are disabled; enable `features.exec_permission_approvals` before using `with_additional_permissions`" | |
| ); | |
| } | |
| fn implicit_sticky_grants_bypass_inline_permission_validation() { | |
| let cwd = tempdir().expect("tempdir"); | |
| let granted_permissions = file_system_permissions(cwd.path()); | |
| let implicit_permissions = implicit_granted_permissions( | |
| SandboxPermissions::UseDefault, | |
| /*additional_permissions*/ None, | |
| &EffectiveAdditionalPermissions { | |
| sandbox_permissions: SandboxPermissions::WithAdditionalPermissions, | |
| additional_permissions: Some(granted_permissions.clone()), | |
| permissions_preapproved: false, | |
| }, | |
| ); | |
| assert_eq!(implicit_permissions, Some(granted_permissions)); | |
| } | |
| fn explicit_inline_permissions_do_not_use_implicit_sticky_grant_path() { | |
| let cwd = tempdir().expect("tempdir"); | |
| let requested_permissions = file_system_permissions(cwd.path()); | |
| let implicit_permissions = implicit_granted_permissions( | |
| SandboxPermissions::WithAdditionalPermissions, | |
| Some(&requested_permissions), | |
| &EffectiveAdditionalPermissions { | |
| sandbox_permissions: SandboxPermissions::WithAdditionalPermissions, | |
| additional_permissions: Some(requested_permissions.clone()), | |
| permissions_preapproved: false, | |
| }, | |
| ); | |
| assert_eq!(implicit_permissions, None); | |
| } | |
| fn relative_deny_glob_grants_remain_preapproved_after_materialization() { | |
| let cwd = tempdir().expect("tempdir"); | |
| let requested_permissions = AdditionalPermissionProfile { | |
| file_system: Some(FileSystemPermissions { | |
| entries: vec![ | |
| FileSystemSandboxEntry { | |
| path: FileSystemPath::Special { | |
| value: FileSystemSpecialPath::project_roots(/*subpath*/ None), | |
| }, | |
| access: FileSystemAccessMode::Write, | |
| missing_path_behavior: None, | |
| }, | |
| FileSystemSandboxEntry { | |
| path: FileSystemPath::GlobPattern { | |
| pattern: "**/*.env".to_string(), | |
| }, | |
| access: FileSystemAccessMode::Deny, | |
| missing_path_behavior: None, | |
| }, | |
| ], | |
| glob_scan_max_depth: None, | |
| }), | |
| ..Default::default() | |
| }; | |
| let cwd_uri = PathUri::from_host_native_path(cwd.path()).expect("cwd URI"); | |
| let stored_grant = intersect_permission_profiles_with_context( | |
| requested_permissions.clone(), | |
| requested_permissions.clone(), | |
| &local_context(&cwd_uri), | |
| ); | |
| let effective_permissions = | |
| merge_permission_profiles(Some(&requested_permissions), Some(&stored_grant)) | |
| .expect("merged permissions"); | |
| assert_eq!( | |
| preapproved_permission_profile( | |
| &effective_permissions, | |
| &stored_grant, | |
| &local_context(&cwd_uri), | |
| ), | |
| Some(stored_grant) | |
| ); | |
| } | |
| fn symbolic_tmpdir_preapproval_requires_executor_metadata() { | |
| let cwd = PathUri::parse("file:///C:/workspace").expect("Windows cwd"); | |
| let temporary_directory = PathUri::parse("file:///C:/Temp").expect("Windows temp dir"); | |
| let concrete = FileSystemSandboxEntry::new( | |
| temporary_directory.clone().into(), | |
| FileSystemAccessMode::Write, | |
| ); | |
| let symbolic = FileSystemSandboxEntry { | |
| path: FileSystemPath::Special { | |
| value: FileSystemSpecialPath::Tmpdir, | |
| }, | |
| access: FileSystemAccessMode::Write, | |
| missing_path_behavior: None, | |
| }; | |
| let effective = AdditionalPermissionProfile { | |
| file_system: Some(FileSystemPermissions { | |
| entries: vec![concrete.clone(), symbolic], | |
| glob_scan_max_depth: None, | |
| }), | |
| ..Default::default() | |
| }; | |
| let granted = AdditionalPermissionProfile { | |
| file_system: Some(FileSystemPermissions { | |
| entries: vec![concrete], | |
| glob_scan_max_depth: None, | |
| }), | |
| ..Default::default() | |
| }; | |
| assert_eq!( | |
| preapproved_permission_profile(&effective, &granted, &local_context(&cwd)), | |
| None | |
| ); | |
| let empty_temporary_directories = []; | |
| let empty_tmpdir_context = FileSystemSandboxPolicyContext { | |
| temporary_directories: Some(&empty_temporary_directories), | |
| ..local_context(&cwd) | |
| }; | |
| assert_eq!( | |
| preapproved_permission_profile(&effective, &granted, &empty_tmpdir_context), | |
| None | |
| ); | |
| let temporary_directories = [temporary_directory]; | |
| let tmpdir_context = FileSystemSandboxPolicyContext { | |
| temporary_directories: Some(&temporary_directories), | |
| ..local_context(&cwd) | |
| }; | |
| assert_eq!( | |
| preapproved_permission_profile(&effective, &granted, &tmpdir_context), | |
| Some(granted) | |
| ); | |
| } | |
| } | |