From 3cee35bfef03af609fb1304c4e320dd4746aa45e Mon Sep 17 00:00:00 2001 From: Charles Cunningham Date: Mon, 23 Mar 2026 19:57:48 -0700 Subject: [PATCH] network-proxy: preserve parent item id on mitm block Propagate the extracted parent tool item id through the CONNECT limited-mode MITM-required block path so core can attribute the denial to the correct active tool call. Also derive PartialEq/Eq for BlockedRequest to keep the focused regression test as a full-object assertion. Co-authored-by: Codex --- codex-rs/network-proxy/src/http_proxy.rs | 25 ++++++++++++++++++++++-- codex-rs/network-proxy/src/runtime.rs | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/codex-rs/network-proxy/src/http_proxy.rs b/codex-rs/network-proxy/src/http_proxy.rs index 9b7c3c1361..c681c14969 100644 --- a/codex-rs/network-proxy/src/http_proxy.rs +++ b/codex-rs/network-proxy/src/http_proxy.rs @@ -297,7 +297,7 @@ async fn http_connect_accept( .record_blocked(BlockedRequest::new(BlockedRequestArgs { host: host.clone(), reason: REASON_MITM_REQUIRED.to_string(), - parent_tool_item_id: None, + parent_tool_item_id, client: client.clone(), method: Some("CONNECT".to_string()), mode: Some(NetworkMode::Limited), @@ -1053,9 +1053,10 @@ mod tests { .method(Method::CONNECT) .uri("https://example.com:443") .header("host", "example.com:443") + .header(header::PROXY_AUTHORIZATION, "Basic Y29tbWFuZC0xOg==") .body(Body::empty()) .unwrap(); - req.extensions_mut().insert(state); + req.extensions_mut().insert(Arc::clone(&state)); let response = http_connect_accept(None, req).await.unwrap_err(); assert_eq!(response.status(), StatusCode::FORBIDDEN); @@ -1063,6 +1064,26 @@ mod tests { response.headers().get("x-proxy-error").unwrap(), "blocked-by-mitm-required" ); + let blocked = state + .blocked_snapshot() + .await + .expect("blocked snapshot should succeed"); + assert_eq!( + blocked, + vec![BlockedRequest { + host: "example.com".to_string(), + reason: REASON_MITM_REQUIRED.to_string(), + parent_tool_item_id: Some("command-1".to_string()), + client: None, + method: Some("CONNECT".to_string()), + mode: Some(NetworkMode::Limited), + protocol: "http-connect".to_string(), + decision: Some("deny".to_string()), + source: Some("mode_guard".to_string()), + port: Some(443), + timestamp: blocked[0].timestamp, + }] + ); } #[tokio::test] diff --git a/codex-rs/network-proxy/src/runtime.rs b/codex-rs/network-proxy/src/runtime.rs index d3e5899366..befc1997d4 100644 --- a/codex-rs/network-proxy/src/runtime.rs +++ b/codex-rs/network-proxy/src/runtime.rs @@ -80,7 +80,7 @@ pub enum HostBlockDecision { Blocked(HostBlockReason), } -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] pub struct BlockedRequest { pub host: String, pub reason: String,