mirror of
https://github.com/openai/codex.git
synced 2026-11-14 21:40:39 +00:00
Validate network socket policies using the executor OS (#46302)
## Why A controller and its executor can run different operating systems. Validating socket paths against the controller's OS can reject absolute paths that are valid on the executor, such as Windows paths on a Linux controller. ## What changed - Thread `NetworkProxyExecutorOs` through network policy validation, proxy construction, and policy updates. - Require allowed socket paths to be NUL-free and absolute for the executor OS, while preserving deny entries unchanged. - Accept either Unix or Windows absolute syntax when executor metadata omits the OS, then validate against the executor's own OS at launch. - Keep native path normalization and socket support checks at execution time. ## Testing Add coverage for cross-platform absolute path syntax, invalid allow entries, preserved deny entries, and remote policy round trips that retain executor semantics through domain edits and proxy construction. GitOrigin-RevId: 1ebc09cbce7138f60ec5fd62875591a3df52d067
This commit is contained in:
@@ -4,9 +4,12 @@ use super::RemoteNetworkProxyConfig;
|
||||
use super::RemoteNetworkProxyLaunchConfig;
|
||||
use crate::MitmHookConfig;
|
||||
use crate::NetworkMode;
|
||||
use crate::NetworkProxy;
|
||||
use crate::NetworkProxyAuditMetadata;
|
||||
use crate::NetworkProxyConfig;
|
||||
use crate::NetworkProxyExecutorOs;
|
||||
use crate::NetworkProxyState;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn optional_socket_policy_preserves_input_and_resolves_at_remote_boundary() {
|
||||
@@ -38,27 +41,65 @@ fn optional_socket_policy_preserves_input_and_resolves_at_remote_boundary() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_preserves_supported_effective_settings() {
|
||||
let mut config = NetworkProxyConfig {
|
||||
enabled: true,
|
||||
enable_socks5: false,
|
||||
enable_socks5_udp: false,
|
||||
allow_upstream_proxy: false,
|
||||
dangerously_allow_all_unix_sockets: Some(true),
|
||||
mode: NetworkMode::Limited,
|
||||
allow_local_binding: true,
|
||||
..NetworkProxyConfig::default()
|
||||
};
|
||||
config.set_allowed_domains(vec!["example.com".into()]);
|
||||
config.set_denied_domains(vec!["blocked.example.com".into()]);
|
||||
config.set_allow_unix_sockets(vec!["/var/run/example.sock".into()]);
|
||||
#[tokio::test]
|
||||
async fn round_trip_preserves_supported_effective_settings() {
|
||||
for (executor_os, socket) in [
|
||||
(NetworkProxyExecutorOs::Linux, "/var/run/example.sock"),
|
||||
(NetworkProxyExecutorOs::Macos, "/var/run/example.sock"),
|
||||
(NetworkProxyExecutorOs::Windows, r"C:\example.sock"),
|
||||
(NetworkProxyExecutorOs::Unknown, r"C:\example.sock"),
|
||||
] {
|
||||
let mut config = NetworkProxyConfig {
|
||||
enabled: true,
|
||||
enable_socks5: false,
|
||||
enable_socks5_udp: false,
|
||||
allow_upstream_proxy: false,
|
||||
dangerously_allow_all_unix_sockets: Some(true),
|
||||
mode: NetworkMode::Limited,
|
||||
allow_local_binding: true,
|
||||
..NetworkProxyConfig::default()
|
||||
};
|
||||
config.set_allowed_domains(vec!["example.com".into()]);
|
||||
config.set_denied_domains(vec!["blocked.example.com".into()]);
|
||||
config.set_allow_unix_sockets(vec![socket.into()]);
|
||||
|
||||
let remote =
|
||||
RemoteNetworkProxyConfig::from_effective_config(&config).expect("supported remote config");
|
||||
let round_trip = remote.into_network_proxy_config();
|
||||
let remote = RemoteNetworkProxyConfig::from_effective_config(&config)
|
||||
.expect("supported remote config");
|
||||
let round_trip = remote.clone().into_network_proxy_config();
|
||||
|
||||
assert_eq!(round_trip, config);
|
||||
assert_eq!(round_trip, config);
|
||||
|
||||
let state = Arc::new(
|
||||
NetworkProxyState::from_remote_launch_config(
|
||||
RemoteNetworkProxyLaunchConfig::new(remote),
|
||||
executor_os,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
// Policy edits and carrier construction must retain the executor's OS even
|
||||
// when the controller itself cannot interpret the socket as a native path.
|
||||
state.add_allowed_domain("added.example.com").await.unwrap();
|
||||
let proxy = NetworkProxy::builder()
|
||||
.state(state)
|
||||
.managed_by_codex(/*managed_by_codex*/ false)
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
config.upsert_domain_permission(
|
||||
"added.example.com".into(),
|
||||
crate::NetworkDomainPermission::Allow,
|
||||
crate::normalize_host,
|
||||
);
|
||||
assert_eq!(
|
||||
proxy
|
||||
.remote_launch_config()
|
||||
.await
|
||||
.unwrap()
|
||||
.proxy
|
||||
.into_network_proxy_config(),
|
||||
config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -136,13 +177,16 @@ fn launch_config_materializes_audit_and_execution_attribution() {
|
||||
model: Some("model-1".to_string()),
|
||||
..NetworkProxyAuditMetadata::default()
|
||||
};
|
||||
let state = NetworkProxyState::from_remote_launch_config(RemoteNetworkProxyLaunchConfig {
|
||||
proxy,
|
||||
audit_metadata: audit_metadata.clone(),
|
||||
environment_id: Some("remote".to_string()),
|
||||
execution_id: Some("execution-1".to_string()),
|
||||
policy_decision_timeout_ms: None,
|
||||
})
|
||||
let state = NetworkProxyState::from_remote_launch_config(
|
||||
RemoteNetworkProxyLaunchConfig {
|
||||
proxy,
|
||||
audit_metadata: audit_metadata.clone(),
|
||||
environment_id: Some("remote".to_string()),
|
||||
execution_id: Some("execution-1".to_string()),
|
||||
policy_decision_timeout_ms: None,
|
||||
},
|
||||
NetworkProxyExecutorOs::from_platform_os(Some(std::env::consts::OS)),
|
||||
)
|
||||
.expect("remote launch state");
|
||||
|
||||
assert_eq!(state.audit_metadata(), &audit_metadata);
|
||||
|
||||
Reference in New Issue
Block a user