mirror of
https://github.com/openai/codex.git
synced 2026-04-28 16:45:54 +00:00
feat(network-proxy): structured policy signaling and attempt correlation to core (#11662)
## Summary When network requests were blocked, downstream code often had to infer ask vs deny from free-form response text. That was brittle and led to incorrect approval behavior. This PR fixes the proxy side so blocked decisions are structured and request metadata survives reliably. ## Description - Blocked proxy responses now carry consistent structured policy decision data. - Request attempt metadata is preserved across proxy env paths (including ALL_PROXY flows). - Header stripping was tightened so we still remove unsafe forwarding headers, but keep metadata needed for policy handling. - Block messages were clarified (for example, allowlist miss vs explicit deny). - Added unified violation log entries so policy failures can be inspected in one place. - Added/updated tests for these behaviors. --------- Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
use crate::admin;
|
||||
use crate::config;
|
||||
use crate::http_proxy;
|
||||
use crate::metadata::proxy_username_for_attempt_id;
|
||||
use crate::network_policy::NetworkPolicyDecider;
|
||||
use crate::runtime::BlockedRequest;
|
||||
use crate::runtime::unix_socket_permissions_supported;
|
||||
use crate::socks5;
|
||||
use crate::state::NetworkProxyState;
|
||||
@@ -312,8 +314,12 @@ fn apply_proxy_env_overrides(
|
||||
socks_addr: SocketAddr,
|
||||
socks_enabled: bool,
|
||||
allow_local_binding: bool,
|
||||
network_attempt_id: Option<&str>,
|
||||
) {
|
||||
let http_proxy_url = format!("http://{http_addr}");
|
||||
let http_proxy_url = network_attempt_id
|
||||
.map(proxy_username_for_attempt_id)
|
||||
.map(|username| format!("http://{username}@{http_addr}"))
|
||||
.unwrap_or_else(|| format!("http://{http_addr}"));
|
||||
let socks_proxy_url = format!("socks5h://{socks_addr}");
|
||||
env.insert(
|
||||
ALLOW_LOCAL_BINDING_ENV_KEY.to_string(),
|
||||
@@ -354,18 +360,25 @@ fn apply_proxy_env_overrides(
|
||||
|
||||
env.insert("ELECTRON_GET_USE_PROXY".to_string(), "true".to_string());
|
||||
|
||||
if socks_enabled {
|
||||
// Keep HTTP_PROXY/HTTPS_PROXY as HTTP endpoints. A lot of clients break if
|
||||
// those vars contain SOCKS URLs. We only switch ALL_PROXY here.
|
||||
//
|
||||
// For attempt-scoped runs, point ALL_PROXY at the HTTP proxy URL so the
|
||||
// attempt metadata survives in proxy credentials for correlation.
|
||||
if socks_enabled && network_attempt_id.is_none() {
|
||||
set_env_keys(env, ALL_PROXY_ENV_KEYS, &socks_proxy_url);
|
||||
set_env_keys(env, FTP_PROXY_ENV_KEYS, &socks_proxy_url);
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// Preserve existing SSH wrappers (for example: Secretive/Teleport setups)
|
||||
// and only provide a SOCKS ProxyCommand fallback when one is not present.
|
||||
env.entry("GIT_SSH_COMMAND".to_string())
|
||||
.or_insert_with(|| format!("ssh -o ProxyCommand='nc -X 5 -x {socks_addr} %h %p'"));
|
||||
}
|
||||
} else {
|
||||
set_env_keys(env, ALL_PROXY_ENV_KEYS, &http_proxy_url);
|
||||
set_env_keys(env, FTP_PROXY_ENV_KEYS, &http_proxy_url);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if socks_enabled {
|
||||
// Preserve existing SSH wrappers (for example: Secretive/Teleport setups)
|
||||
// and only provide a SOCKS ProxyCommand fallback when one is not present.
|
||||
env.entry("GIT_SSH_COMMAND".to_string())
|
||||
.or_insert_with(|| format!("ssh -o ProxyCommand='nc -X 5 -x {socks_addr} %h %p'"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +399,22 @@ impl NetworkProxy {
|
||||
self.admin_addr
|
||||
}
|
||||
|
||||
pub async fn latest_blocked_request_for_attempt(
|
||||
&self,
|
||||
attempt_id: &str,
|
||||
) -> Result<Option<BlockedRequest>> {
|
||||
self.state.latest_blocked_for_attempt(attempt_id).await
|
||||
}
|
||||
|
||||
pub fn apply_to_env(&self, env: &mut HashMap<String, String>) {
|
||||
self.apply_to_env_for_attempt(env, None);
|
||||
}
|
||||
|
||||
pub fn apply_to_env_for_attempt(
|
||||
&self,
|
||||
env: &mut HashMap<String, String>,
|
||||
network_attempt_id: Option<&str>,
|
||||
) {
|
||||
// Enforce proxying for child processes. We intentionally override existing values so
|
||||
// command-level environment cannot bypass the managed proxy endpoint.
|
||||
apply_proxy_env_overrides(
|
||||
@@ -395,6 +423,7 @@ impl NetworkProxy {
|
||||
self.socks_addr,
|
||||
self.socks_enabled,
|
||||
self.allow_local_binding,
|
||||
network_attempt_id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -694,6 +723,7 @@ mod tests {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8081),
|
||||
true,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -736,6 +766,7 @@ mod tests {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8081),
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -745,6 +776,39 @@ mod tests {
|
||||
assert_eq!(env.get(ALLOW_LOCAL_BINDING_ENV_KEY), Some(&"1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_proxy_env_overrides_embeds_attempt_id_in_http_proxy_url() {
|
||||
let mut env = HashMap::new();
|
||||
apply_proxy_env_overrides(
|
||||
&mut env,
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 3128),
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8081),
|
||||
true,
|
||||
false,
|
||||
Some("attempt-123"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
env.get("HTTP_PROXY"),
|
||||
Some(&"http://codex-net-attempt-attempt-123@127.0.0.1:3128".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("HTTPS_PROXY"),
|
||||
Some(&"http://codex-net-attempt-attempt-123@127.0.0.1:3128".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("ALL_PROXY"),
|
||||
Some(&"http://codex-net-attempt-attempt-123@127.0.0.1:3128".to_string())
|
||||
);
|
||||
#[cfg(target_os = "macos")]
|
||||
assert_eq!(
|
||||
env.get("GIT_SSH_COMMAND"),
|
||||
Some(&"ssh -o ProxyCommand='nc -X 5 -x 127.0.0.1:8081 %h %p'".to_string())
|
||||
);
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
assert_eq!(env.get("GIT_SSH_COMMAND"), None);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn apply_proxy_env_overrides_preserves_existing_git_ssh_command() {
|
||||
@@ -759,6 +823,7 @@ mod tests {
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8081),
|
||||
true,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user