Honor managed permission profiles in network proxy resolution (#34436)

## Why

Permission profiles defined by `requirements.toml` can be selected as active,
but their network configuration was not included when resolving the network
proxy specification for that selection.

## What changed

Merge managed permission profiles from `requirements.toml` with configured
profiles before looking up the active profile's network proxy settings.

## Testing

- Verify config loading retains the proxy settings of an active managed
  profile.
- Verify `turn/start` accepts a managed profile with network settings.

GitOrigin-RevId: 3374f82a8fb0ce8f338fa2cea77c28ea2eb89636
This commit is contained in:
viyatb-oai
2026-07-21 01:05:03 +00:00
committed by copyberry
parent c9ef7eff00
commit 88fac6fe10
3 changed files with 117 additions and 8 deletions

View File

@@ -1433,6 +1433,96 @@ async fn turn_start_rejects_invalid_permission_selection_before_starting_turn()
Ok(())
}
#[tokio::test]
async fn turn_start_accepts_managed_network_profile_from_requirements() -> Result<()> {
let responses = vec![create_final_assistant_message_sse_response("Done")?];
let server = create_mock_responses_server_sequence_unchecked(responses).await;
let codex_home = TempDir::new()?;
create_config_toml(
codex_home.path(),
&server.uri(),
"never",
&BTreeMap::from([(Feature::NetworkProxy, true)]),
)?;
std::fs::write(
codex_home.path().join("requirements.toml"),
r#"
default_permissions = "managed-network"
[allowed_permission_profiles]
managed-network = true
":read-only" = true
[permissions.managed-network]
extends = ":read-only"
[permissions.managed-network.network]
enabled = true
allow_local_binding = false
[permissions.managed-network.network.domains]
"packages.example" = "allow"
"#,
)?;
let mut app_server = TestAppServer::builder()
.with_codex_home(codex_home.path())
.build()
.await?;
timeout(DEFAULT_READ_TIMEOUT, app_server.initialize()).await??;
let thread_req = app_server
.send_thread_start_request_with_auto_env(ThreadStartParams {
model: Some("mock-model".to_string()),
..Default::default()
})
.await?;
let thread_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
app_server.read_stream_until_response_message(RequestId::Integer(thread_req)),
)
.await??;
let ThreadStartResponse {
thread,
active_permission_profile,
..
} = to_response::<ThreadStartResponse>(thread_resp)?;
let active_permission_profile =
active_permission_profile.context("expected active permission profile")?;
assert_eq!(active_permission_profile.id, "managed-network");
let turn_req = app_server
.send_turn_start_request(TurnStartParams {
thread_id: thread.id,
client_user_message_id: None,
input: vec![V2UserInput::Text {
text: "Use the managed network profile".to_string(),
text_elements: Vec::new(),
}],
permissions: Some("managed-network".to_string()),
..Default::default()
})
.await?;
let turn_resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
app_server.read_stream_until_response_message(RequestId::Integer(turn_req)),
)
.await??;
let TurnStartResponse { turn } = to_response::<TurnStartResponse>(turn_resp)?;
assert!(
!turn.id.is_empty(),
"turn/start should resolve the managed profile's network configuration"
);
timeout(
DEFAULT_READ_TIMEOUT,
app_server.read_stream_until_notification_message("turn/completed"),
)
.await??;
Ok(())
}
#[tokio::test]
async fn turn_start_rejects_unknown_environment_before_starting_turn() -> Result<()> {
let server = create_mock_responses_server_repeating_assistant("Done").await;

View File

@@ -1364,6 +1364,9 @@ async fn system_requirements_define_managed_permission_profiles() -> anyhow::Res
codex_home.join(CONFIG_TOML_FILE),
r#"
default_permissions = "managed-standard"
[features]
network_proxy = true
"#,
)
.await?;
@@ -1378,6 +1381,11 @@ managed-standard = true
[permissions.managed-standard]
extends = ":workspace"
[permissions.managed-standard.network]
enabled = true
proxy_url = "http://127.0.0.1:43128"
enable_socks5 = false
"#,
)
.await?;
@@ -1399,13 +1407,20 @@ extends = ":workspace"
.allowed_permission_profiles,
Some(BTreeMap::from([("managed-standard".to_string(), true)]))
);
assert_eq!(
config
.permissions
.active_permission_profile()
.map(|profile| profile.id),
Some("managed-standard".to_string())
);
let active_permission_profile = config
.permissions
.active_permission_profile()
.expect("managed profile should be active");
assert_eq!(active_permission_profile.id, "managed-standard");
let network = config
.network_proxy_spec_for_active_permission_profile(
&active_permission_profile,
config.permissions.permission_profile(),
)?
.expect("managed profile should retain its network proxy configuration");
assert_eq!(network.proxy_host_and_port(), "127.0.0.1:43128");
assert!(!network.socks_enabled());
Ok(())
}

View File

@@ -4204,8 +4204,12 @@ impl Config {
),
)
})?;
let mut configured_network_proxy_config = network_proxy_config_for_profile_selection(
let permissions = merge_managed_permission_profiles(
cfg.permissions.as_ref(),
self.config_layer_stack.requirements_toml(),
)?;
let mut configured_network_proxy_config = network_proxy_config_for_profile_selection(
permissions.as_ref(),
active_permission_profile.id.as_str(),
)?;
if self.features.enabled(Feature::NetworkProxy)