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:
viyatb-oai
2026-02-13 01:01:11 -08:00
committed by GitHub
parent fca5629e34
commit 2bced810da
14 changed files with 581 additions and 83 deletions

View File

@@ -168,6 +168,10 @@ 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),
}))
.await;
let client = client.as_deref().unwrap_or_default();
@@ -198,6 +202,10 @@ 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),
}))
.await;
let client = client.as_deref().unwrap_or_default();
@@ -221,6 +229,7 @@ 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 {
@@ -245,6 +254,10 @@ 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),
}))
.await;
let client = client.as_deref().unwrap_or_default();
@@ -305,6 +318,10 @@ 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),
}))
.await;
let client = client.as_deref().unwrap_or_default();
@@ -335,6 +352,10 @@ 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),
}))
.await;
return Err(policy_denied_error(REASON_METHOD_NOT_ALLOWED, &details));
@@ -354,6 +375,7 @@ 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 {
@@ -378,6 +400,10 @@ 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),
}))
.await;
let client = client.as_deref().unwrap_or_default();