use anyhow::Context; use anyhow::Result; use codex_config::CloudConfigBundleLoader; use codex_config::LoaderOverrides; use codex_config::test_support::CloudConfigBundleFixture; use codex_config::types::WindowsSandboxModeToml; use codex_core::CodexThreadSettingsOverrides; use codex_core::config::ConfigBuilder; use codex_features::Feature; use codex_protocol::models::PermissionProfile; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::ThreadSettingsOverrides; use codex_protocol::sandbox::SandboxType; use codex_utils_absolute_path::AbsolutePathBuf; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_target_windows; use core_test_support::submit_thread_settings; use core_test_support::test_codex::test_codex; use pretty_assertions::assert_eq; use std::sync::Arc; use std::sync::RwLock; use tempfile::TempDir; #[tokio::test] async fn managed_mxc_opt_out_blocks_preferred_and_explicit_selection() -> Result<()> { for mode in [None, Some("elevated"), Some("mxc")] { let home = TempDir::new()?; let sandbox = mode .map(|mode| format!("sandbox = {mode:?}\n")) .unwrap_or_default(); std::fs::write( home.path().join("config.toml"), format!("[windows]\n{sandbox}[features]\nprefer_mxc = true\n"), )?; let result = ConfigBuilder::default() .loader_overrides(LoaderOverrides::without_managed_config_for_tests()) .codex_home(home.path().to_path_buf()) .fallback_cwd(Some(home.path().to_path_buf())) .cloud_config_bundle( CloudConfigBundleFixture::loader_with_enterprise_requirement( "[windows]\nallow_mxc = false\n[features]\nelevated_windows_sandbox = true\n", ), ) .build() .await; if mode == Some("mxc") { let error = result.expect_err("explicit MXC should be rejected by managed requirements"); assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput); assert!(error.to_string().contains("windows.allow_mxc = false")); } else { let config = result?; assert_eq!( ( config.permissions.windows_sandbox_mode, config.prefer_mxc, config.effective_local_windows_sandbox_type() ), ( mode.map(|_| WindowsSandboxModeToml::Elevated), false, SandboxType::WindowsRestrictedToken, ), ); } } Ok(()) } #[tokio::test] async fn refreshed_cloud_bundle_updates_later_sessions() -> Result<()> { let server = start_mock_server().await; let home = Arc::new(TempDir::new()?); let initial_bundle = CloudConfigBundleFixture::enterprise_requirement( r#"allowed_approval_policies = ["never"]"#, ) .add_enterprise_config(r#"developer_instructions = "initial managed instructions""#) .into_bundle(); let latest = Arc::new(RwLock::new(initial_bundle)); let getter_latest = Arc::clone(&latest); let loader = CloudConfigBundleLoader::from_getter(move || { let latest = Arc::clone(&getter_latest); async move { Ok(Some(latest.read().expect("bundle state lock").clone())) } }); let mut initial_builder = test_codex() .with_home(Arc::clone(&home)) .with_cloud_config_bundle(loader.clone()); let initial = initial_builder.build_with_auto_env(&server).await?; assert_eq!( initial.session_configured.approval_policy, AskForApproval::Never ); assert_eq!( initial .codex .config() .await .developer_instructions .as_deref(), Some("initial managed instructions") ); *latest.write().expect("bundle state lock") = CloudConfigBundleFixture::enterprise_requirement( r#"allowed_approval_policies = ["on-request"]"#, ) .add_enterprise_config(r#"developer_instructions = "refreshed managed instructions""#) .into_bundle(); let mut refreshed_builder = test_codex() .with_home(home) .with_cloud_config_bundle(loader); let refreshed = refreshed_builder.build_with_auto_env(&server).await?; assert_eq!( refreshed.session_configured.approval_policy, AskForApproval::OnRequest ); assert_eq!( refreshed .codex .config() .await .developer_instructions .as_deref(), Some("refreshed managed instructions") ); Ok(()) } #[tokio::test] async fn managed_deny_read_requirements_follow_thread_permission_updates() -> Result<()> { skip_if_target_windows!( Ok(()), "Windows restricted-token sandbox cannot enforce deny-read policies" ); let server = start_mock_server().await; let home = Arc::new(TempDir::new()?); let denied_root = home.path().join("managed-private"); let nested_root = denied_root.join("nested"); std::fs::create_dir_all(&nested_root)?; let nested_root = AbsolutePathBuf::from_absolute_path(nested_root)?; let mut builder = test_codex() .with_home(home) .with_cloud_config_bundle( CloudConfigBundleFixture::loader_with_enterprise_requirement(format!( "[permissions.filesystem]\ndeny_read = [{denied_root:?}]\n" )), ) .with_config(|config| { config .permissions .set_permission_profile(PermissionProfile::workspace_write()) .expect("safe workspace permissions should preserve managed denies"); }); let test = builder.build_with_auto_env(&server).await?; submit_thread_settings( &test.codex, ThreadSettingsOverrides { permission_profile: Some(PermissionProfile::read_only()), ..Default::default() }, ) .await?; let snapshot = test.codex.config_snapshot().await; assert!( !snapshot .permission_profile .file_system_sandbox_policy() .can_read_local_path_with_cwd(nested_root.as_path(), snapshot.cwd().as_path()), "a live permission change must preserve managed deny-read rules" ); let conflicting_profile = PermissionProfile::workspace_write_with( std::slice::from_ref(&nested_root), NetworkSandboxPolicy::Restricted, /*exclude_tmpdir_env_var*/ false, /*exclude_slash_tmp*/ false, ); let error = test .codex .preview_thread_settings_overrides(CodexThreadSettingsOverrides { permission_profile: Some(conflicting_profile), ..Default::default() }) .await .err() .context("a concrete writable root must not override a managed deny")?; assert!(error.to_string().contains("permissions.filesystem")); Ok(()) } #[tokio::test] async fn managed_guardian_v1_requirements_disable_guardian_v2() -> Result<()> { let server = start_mock_server().await; for (requirements, guardian_v2_enabled) in [ (r#"allowed_approvals_reviewers = ["auto_review"]"#, false), ( r#"allowed_approvals_reviewers = ["guardian_subagent"]"#, false, ), ( r#"allowed_approvals_reviewers = ["auto_review", "user"]"#, true, ), ("[features]\nguardian_approval = true\n", true), ("[features]\nauto_review = true\n", true), ] { let mut builder = test_codex() .with_pre_build_hook(|home| { std::fs::write(home.join("config.toml"), "[features]\nguardianv2 = true\n") .expect("Guardian v2 configuration should be written"); }) .with_cloud_config_bundle( CloudConfigBundleFixture::loader_with_enterprise_requirement(requirements), ); let test = builder.build_with_auto_env(&server).await?; assert!(test.config.features.enabled(Feature::GuardianApproval)); assert_eq!( test.config.features.enabled(Feature::GuardianV2), guardian_v2_enabled, "{requirements}" ); } Ok(()) }