mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
## What changed - Define the `network/policyRequest` RPC payloads for associating a network request with a process and returning an `allow`, `deny`, or `ask` decision. - Cover HTTP, HTTPS CONNECT, and SOCKS5 TCP/UDP requests, with shared size limits for callback fields. - Add the backward-compatible `requestPolicyDecisions` remote proxy option so executors can opt in to sending policy decisions to clients. - Re-export the new protocol types from `codex-exec-server`. ## Testing - Verify the request and decision JSON shapes. - Verify that the proxy opt-in is omitted and defaults to `false` for legacy configurations, while an enabled value round-trips. GitOrigin-RevId: 65b0a0723d1abb6e4f504a41d3a0dd41f5f21bf7
144 lines
4.7 KiB
Rust
144 lines
4.7 KiB
Rust
use pretty_assertions::assert_eq;
|
|
|
|
use super::RemoteNetworkProxyConfig;
|
|
use super::RemoteNetworkProxyLaunchConfig;
|
|
use crate::MitmHookConfig;
|
|
use crate::NetworkMode;
|
|
use crate::NetworkProxyAuditMetadata;
|
|
use crate::NetworkProxyConfig;
|
|
use crate::NetworkProxyState;
|
|
|
|
#[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: 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()]);
|
|
|
|
let remote =
|
|
RemoteNetworkProxyConfig::from_effective_config(&config).expect("supported remote config");
|
|
let round_trip = remote.into_network_proxy_config();
|
|
|
|
assert_eq!(round_trip, config);
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_unsupported_configuration() {
|
|
let cases = [
|
|
(
|
|
"MITM",
|
|
NetworkProxyConfig {
|
|
enabled: true,
|
|
mitm: true,
|
|
..NetworkProxyConfig::default()
|
|
},
|
|
),
|
|
(
|
|
"credential broker",
|
|
NetworkProxyConfig {
|
|
enabled: true,
|
|
credential_broker: true,
|
|
..NetworkProxyConfig::default()
|
|
},
|
|
),
|
|
(
|
|
"plaintext credential injection",
|
|
NetworkProxyConfig {
|
|
enabled: true,
|
|
dangerously_allow_plaintext_credential_injection: true,
|
|
..NetworkProxyConfig::default()
|
|
},
|
|
),
|
|
(
|
|
"MITM hooks",
|
|
NetworkProxyConfig {
|
|
enabled: true,
|
|
mitm_hooks: vec![MitmHookConfig::default()],
|
|
..NetworkProxyConfig::default()
|
|
},
|
|
),
|
|
];
|
|
|
|
for (feature, config) in cases {
|
|
assert!(
|
|
RemoteNetworkProxyConfig::from_effective_config(&config).is_err(),
|
|
"{feature} must not cross the remote executor boundary"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn accepts_unsupported_configuration_when_proxy_is_disabled() {
|
|
let config = NetworkProxyConfig {
|
|
mitm: true,
|
|
credential_broker: true,
|
|
dangerously_allow_plaintext_credential_injection: true,
|
|
mitm_hooks: vec![MitmHookConfig::default()],
|
|
..NetworkProxyConfig::default()
|
|
};
|
|
|
|
let remote = RemoteNetworkProxyConfig::from_effective_config(&config)
|
|
.expect("disabled proxy configuration does not cross the executor boundary");
|
|
|
|
assert!(!remote.enabled);
|
|
}
|
|
|
|
#[test]
|
|
fn launch_config_materializes_audit_and_execution_attribution() {
|
|
let proxy = RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig {
|
|
enabled: true,
|
|
..NetworkProxyConfig::default()
|
|
})
|
|
.expect("supported remote config");
|
|
let audit_metadata = NetworkProxyAuditMetadata {
|
|
conversation_id: Some("conversation-1".to_string()),
|
|
user_account_id: Some("account-1".to_string()),
|
|
originator: Some("codex_cli_rs".to_string()),
|
|
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()),
|
|
})
|
|
.expect("remote launch state");
|
|
|
|
assert_eq!(state.audit_metadata(), &audit_metadata);
|
|
assert_eq!(state.environment_id(), Some("remote"));
|
|
assert_eq!(state.execution_id().as_deref(), Some("execution-1"));
|
|
}
|
|
|
|
#[test]
|
|
fn policy_decision_callback_opt_in_is_backward_compatible() {
|
|
let mut config =
|
|
RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig::default())
|
|
.expect("supported remote config");
|
|
let legacy = serde_json::to_value(&config).expect("serialize legacy config");
|
|
assert_eq!(legacy.get("requestPolicyDecisions"), None);
|
|
assert!(
|
|
!serde_json::from_value::<RemoteNetworkProxyConfig>(legacy)
|
|
.expect("deserialize legacy remote config")
|
|
.request_policy_decisions
|
|
);
|
|
|
|
config.request_policy_decisions = true;
|
|
let enabled = serde_json::to_value(&config).expect("serialize callback-enabled config");
|
|
assert_eq!(enabled["requestPolicyDecisions"], true);
|
|
assert_eq!(
|
|
serde_json::from_value::<RemoteNetworkProxyConfig>(enabled)
|
|
.expect("deserialize callback-enabled config"),
|
|
config
|
|
);
|
|
}
|