mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Handle exec-server network policy requests in the client (#35359)
## What changed - Add client-side handling for exec-server network policy requests, including request validation, per-process decision routing, and allow, deny, or ask responses. - Bound concurrent callbacks and fail closed when requests are invalid, exceed capacity, time out, or outlive their process session. - Replace the callback opt-in flag with a launch-level `policyDecisionTimeoutMs` value and include transport overhead in the executor timeout. - Preserve callback admission limits across connection recovery and clean up policy state when process startup is abandoned or a session ends. ## Testing - Cover decision forwarding, invalid and excess requests, cancellation during process cleanup, abandoned starts, timeout propagation, and launch-config serialization. GitOrigin-RevId: 6ee2a0139e0f8170cf1a5cdd646bd811b7d3c1f6
This commit is contained in:
@@ -831,6 +831,7 @@ impl NetworkProxy {
|
||||
audit_metadata: self.state.audit_metadata().clone(),
|
||||
environment_id,
|
||||
execution_id,
|
||||
policy_decision_timeout_ms: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ pub struct RemoteNetworkProxyLaunchConfig {
|
||||
pub environment_id: Option<String>,
|
||||
#[serde(default)]
|
||||
pub execution_id: Option<String>,
|
||||
/// Controller-side policy decision budget. The executor adds transport overhead.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub policy_decision_timeout_ms: Option<u64>,
|
||||
}
|
||||
|
||||
impl RemoteNetworkProxyLaunchConfig {
|
||||
@@ -33,6 +36,7 @@ impl RemoteNetworkProxyLaunchConfig {
|
||||
audit_metadata: NetworkProxyAuditMetadata::default(),
|
||||
environment_id: None,
|
||||
execution_id: None,
|
||||
policy_decision_timeout_ms: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,9 +70,6 @@ pub struct RemoteNetworkProxyConfig {
|
||||
pub domains: Option<NetworkDomainPermissions>,
|
||||
pub unix_sockets: Option<NetworkUnixSocketPermissions>,
|
||||
pub allow_local_binding: bool,
|
||||
/// Whether the executor sends domain policy decisions back to the client.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub request_policy_decisions: bool,
|
||||
}
|
||||
|
||||
impl RemoteNetworkProxyConfig {
|
||||
@@ -91,7 +92,6 @@ impl RemoteNetworkProxyConfig {
|
||||
domains: config.domains.clone(),
|
||||
unix_sockets: config.unix_sockets.clone(),
|
||||
allow_local_binding: config.allow_local_binding,
|
||||
request_policy_decisions: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ fn launch_config_materializes_audit_and_execution_attribution() {
|
||||
audit_metadata: audit_metadata.clone(),
|
||||
environment_id: Some("remote".to_string()),
|
||||
execution_id: Some("execution-1".to_string()),
|
||||
policy_decision_timeout_ms: None,
|
||||
})
|
||||
.expect("remote launch state");
|
||||
|
||||
@@ -120,24 +121,18 @@ fn launch_config_materializes_audit_and_execution_attribution() {
|
||||
}
|
||||
|
||||
#[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);
|
||||
fn policy_decision_callback_timeout_round_trips() {
|
||||
let config = RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig::default())
|
||||
.expect("supported remote config");
|
||||
let mut launch = RemoteNetworkProxyLaunchConfig::new(config);
|
||||
let without_timeout = serde_json::to_value(&launch).expect("serialize launch config");
|
||||
assert_eq!(without_timeout.get("policyDecisionTimeoutMs"), None);
|
||||
launch.policy_decision_timeout_ms = Some(900_000);
|
||||
let with_timeout = serde_json::to_value(&launch).expect("serialize launch timeout");
|
||||
assert_eq!(with_timeout["policyDecisionTimeoutMs"], 900_000);
|
||||
assert_eq!(
|
||||
serde_json::from_value::<RemoteNetworkProxyConfig>(enabled)
|
||||
.expect("deserialize callback-enabled config"),
|
||||
config
|
||||
serde_json::from_value::<RemoteNetworkProxyLaunchConfig>(with_timeout)
|
||||
.expect("deserialize launch timeout"),
|
||||
launch
|
||||
);
|
||||
}
|
||||
|
||||
@@ -284,6 +284,7 @@ impl NetworkProxyState {
|
||||
audit_metadata,
|
||||
environment_id,
|
||||
execution_id,
|
||||
policy_decision_timeout_ms: _,
|
||||
} = launch;
|
||||
anyhow::ensure!(
|
||||
proxy.enabled,
|
||||
|
||||
Reference in New Issue
Block a user