Refactor network approvals to host/protocol/port scope (#12140)

## Summary
Simplify network approvals by removing per-attempt proxy correlation and
moving to session-level approval dedupe keyed by (host, protocol, port).
Instead of encoding attempt IDs into proxy credentials/URLs, we now
treat approvals as a destination policy decision.

- Concurrent calls to the same destination share one approval prompt.
- Different destinations (or same host on different ports) get separate
prompts.
- Allow once approves the current queued request group only.
- Allow for session caches that (host, protocol, port) and auto-allows
future matching requests.
- Never policy continues to deny without prompting.

Example:
- 3 calls: 
  - a.com (line 443)
  - b.com (line 443)
  - a.com (line 443)
=> 2 prompts total (a, b), second a waits on the first decision.
- a.com:80 is treated separately from a.com line 443

## Testing
- `just fmt` (in `codex-rs`)
- `cargo test -p codex-core tools::network_approval::tests`
- `cargo test -p codex-core` (unit tests pass; existing
integration-suite failures remain in this environment)
This commit is contained in:
viyatb-oai
2026-02-20 10:39:55 -08:00
committed by GitHub
parent 41f15bf07b
commit e8afaed502
40 changed files with 570 additions and 739 deletions

View File

@@ -168,7 +168,6 @@ async fn handle_socks5_tcp(
method: None,
mode: None,
protocol: "socks5".to_string(),
attempt_id: None,
decision: Some(details.decision.as_str().to_string()),
source: Some(details.source.as_str().to_string()),
port: Some(port),
@@ -202,7 +201,6 @@ async fn handle_socks5_tcp(
method: None,
mode: Some(NetworkMode::Limited),
protocol: "socks5".to_string(),
attempt_id: None,
decision: Some(details.decision.as_str().to_string()),
source: Some(details.source.as_str().to_string()),
port: Some(port),
@@ -229,7 +227,6 @@ async fn handle_socks5_tcp(
method: None,
command: None,
exec_policy_hint: None,
attempt_id: None,
});
match evaluate_host_policy(&app_state, policy_decider.as_ref(), &request).await {
@@ -254,7 +251,6 @@ async fn handle_socks5_tcp(
method: None,
mode: None,
protocol: "socks5".to_string(),
attempt_id: None,
decision: Some(details.decision.as_str().to_string()),
source: Some(details.source.as_str().to_string()),
port: Some(port),
@@ -318,7 +314,6 @@ async fn inspect_socks5_udp(
method: None,
mode: None,
protocol: "socks5-udp".to_string(),
attempt_id: None,
decision: Some(details.decision.as_str().to_string()),
source: Some(details.source.as_str().to_string()),
port: Some(port),
@@ -352,7 +347,6 @@ async fn inspect_socks5_udp(
method: None,
mode: Some(NetworkMode::Limited),
protocol: "socks5-udp".to_string(),
attempt_id: None,
decision: Some(details.decision.as_str().to_string()),
source: Some(details.source.as_str().to_string()),
port: Some(port),
@@ -375,7 +369,6 @@ async fn inspect_socks5_udp(
method: None,
command: None,
exec_policy_hint: None,
attempt_id: None,
});
match evaluate_host_policy(&state, policy_decider.as_ref(), &request).await {
@@ -400,7 +393,6 @@ async fn inspect_socks5_udp(
method: None,
mode: None,
protocol: "socks5-udp".to_string(),
attempt_id: None,
decision: Some(details.decision.as_str().to_string()),
source: Some(details.source.as_str().to_string()),
port: Some(port),