mirror of
https://github.com/openai/codex.git
synced 2026-05-02 04:11:39 +03: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:
50
codex-rs/network-proxy/src/metadata.rs
Normal file
50
codex-rs/network-proxy/src/metadata.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use rama_http::HeaderValue;
|
||||
|
||||
pub const NETWORK_ATTEMPT_USERNAME_PREFIX: &str = "codex-net-attempt-";
|
||||
|
||||
pub fn proxy_username_for_attempt_id(attempt_id: &str) -> String {
|
||||
format!("{NETWORK_ATTEMPT_USERNAME_PREFIX}{attempt_id}")
|
||||
}
|
||||
|
||||
pub fn attempt_id_from_proxy_authorization(header: Option<&HeaderValue>) -> Option<String> {
|
||||
let header = header?;
|
||||
let raw = header.to_str().ok()?;
|
||||
let encoded = raw.strip_prefix("Basic ")?;
|
||||
let decoded = STANDARD.decode(encoded.trim()).ok()?;
|
||||
let decoded = String::from_utf8(decoded).ok()?;
|
||||
let username = decoded
|
||||
.split_once(':')
|
||||
.map(|(user, _)| user)
|
||||
.unwrap_or(decoded.as_str());
|
||||
let attempt_id = username.strip_prefix(NETWORK_ATTEMPT_USERNAME_PREFIX)?;
|
||||
if attempt_id.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(attempt_id.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
|
||||
#[test]
|
||||
fn parses_attempt_id_from_proxy_authorization_header() {
|
||||
let encoded = STANDARD.encode(format!("{NETWORK_ATTEMPT_USERNAME_PREFIX}abc123:"));
|
||||
let header = HeaderValue::from_str(&format!("Basic {encoded}")).unwrap();
|
||||
assert_eq!(
|
||||
attempt_id_from_proxy_authorization(Some(&header)),
|
||||
Some("abc123".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_non_attempt_proxy_authorization_header() {
|
||||
let encoded = STANDARD.encode("normal-user:password");
|
||||
let header = HeaderValue::from_str(&format!("Basic {encoded}")).unwrap();
|
||||
assert_eq!(attempt_id_from_proxy_authorization(Some(&header)), None);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user