From d2dc88a8811049d4713fa2fcdd38c8c9e7129aff Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Sun, 19 Apr 2026 22:11:20 -0700 Subject: [PATCH] Respect explicit untrusted project config --- .../app-server/src/codex_message_processor.rs | 2 +- .../app-server/tests/suite/v2/thread_start.rs | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 967329b0bec1..197552782fa5 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -2597,7 +2597,7 @@ impl CodexMessageProcessor { requested_permissions_trust_project(&typesafe_overrides, config.cwd.as_path()); if requested_cwd.is_some() - && !config.active_project.is_trusted() + && config.active_project.trust_level.is_none() && (requested_permissions_trust_project || matches!( config.permissions.sandbox_policy.get(), diff --git a/codex-rs/app-server/tests/suite/v2/thread_start.rs b/codex-rs/app-server/tests/suite/v2/thread_start.rs index 5180df7c9a3d..2aae14e209c3 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_start.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_start.rs @@ -802,6 +802,44 @@ async fn thread_start_with_read_only_sandbox_does_not_persist_project_trust() -> Ok(()) } +#[tokio::test] +async fn thread_start_preserves_untrusted_project_trust() -> Result<()> { + let server = create_mock_responses_server_repeating_assistant("Done").await; + + let codex_home = TempDir::new()?; + create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?; + + let workspace = TempDir::new()?; + let config_path = codex_home.path().join("config.toml"); + let workspace_key = workspace.path().display().to_string(); + let mut config_toml = + std::fs::read_to_string(&config_path)?.parse::()?; + config_toml["projects"][workspace_key.as_str()]["trust_level"] = toml_edit::value("untrusted"); + std::fs::write(&config_path, config_toml.to_string())?; + let config_before = std::fs::read_to_string(&config_path)?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; + + let request_id = mcp + .send_thread_start_request(ThreadStartParams { + cwd: Some(workspace.path().display().to_string()), + sandbox: Some(SandboxMode::WorkspaceWrite), + ..Default::default() + }) + .await?; + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(request_id)), + ) + .await??; + + let config_after = std::fs::read_to_string(&config_path)?; + assert_eq!(config_after, config_before); + + Ok(()) +} + #[tokio::test] async fn thread_start_skips_trust_write_when_project_is_already_trusted() -> Result<()> { let server = create_mock_responses_server_repeating_assistant("Done").await;