core/src/network_policy_decision.rs
106 lines
use codex_execpolicy::Decision as ExecPolicyDecision;use codex_execpolicy::NetworkRuleProtocol as ExecPolicyNetworkRuleProtocol;use codex_network_proxy::BlockedRequest;use codex_network_proxy::NetworkPolicyDecision;use codex_protocol::approvals::NetworkApprovalContext;use codex_protocol::approvals::NetworkApprovalProtocol;use codex_protocol::approvals::NetworkPolicyAmendment;use codex_protocol::approvals::NetworkPolicyRuleAction;use codex_protocol::network_policy::NetworkPolicyDecisionPayload;#[derive(Debug, Clone, PartialEq, Eq)]pub(crate) struct ExecPolicyNetworkRuleAmendment { pub protocol: ExecPolicyNetworkRuleProtocol, pub decision: ExecPolicyDecision, pub justification: String,}fn parse_network_policy_decision(value: &str) -> Option<NetworkPolicyDecision> { match value { "deny" => Some(NetworkPolicyDecision::Deny), "ask" => Some(NetworkPolicyDecision::Ask), _ => None, }}pub(crate) fn network_approval_context_from_payload( payload: &NetworkPolicyDecisionPayload,) -> Option<NetworkApprovalContext> { if !payload.is_ask_from_decider() { return None; } let protocol = payload.protocol?; let host = payload.host.as_deref()?.trim(); if host.is_empty() { return None; } Some(NetworkApprovalContext { host: host.to_string(), protocol, })}pub(crate) fn denied_network_policy_message(blocked: &BlockedRequest) -> Option<String> { let decision = blocked .decision .as_deref() .and_then(parse_network_policy_decision); if decision != Some(NetworkPolicyDecision::Deny) { return None; } let host = blocked.host.trim(); if host.is_empty() { return Some("Network access was blocked by policy.".to_string()); } let detail = match blocked.reason.as_str() { "denied" => "domain is explicitly denied by policy and cannot be approved from this prompt", "not_allowed" => "domain is not on the allowlist for the current sandbox mode", "not_allowed_local" => "local/private network addresses are blocked by the sandbox policy", "method_not_allowed" => "request method is blocked by the current network mode", "proxy_disabled" => "network proxy is disabled", _ => "request is blocked by network policy", }; Some(format!( "Network access to \"{host}\" was blocked: {detail}." ))}pub(crate) fn execpolicy_network_rule_amendment( amendment: &NetworkPolicyAmendment, network_approval_context: &NetworkApprovalContext, host: &str,) -> ExecPolicyNetworkRuleAmendment { let protocol = match network_approval_context.protocol { NetworkApprovalProtocol::Http => ExecPolicyNetworkRuleProtocol::Http, NetworkApprovalProtocol::Https => ExecPolicyNetworkRuleProtocol::Https, NetworkApprovalProtocol::Socks5Tcp => ExecPolicyNetworkRuleProtocol::Socks5Tcp, NetworkApprovalProtocol::Socks5Udp => ExecPolicyNetworkRuleProtocol::Socks5Udp, }; let (decision, action_verb) = match amendment.action { NetworkPolicyRuleAction::Allow => (ExecPolicyDecision::Allow, "Allow"), NetworkPolicyRuleAction::Deny => (ExecPolicyDecision::Forbidden, "Deny"), }; let protocol_label = match network_approval_context.protocol { NetworkApprovalProtocol::Http => "http", NetworkApprovalProtocol::Https => "https_connect", NetworkApprovalProtocol::Socks5Tcp => "socks5_tcp", NetworkApprovalProtocol::Socks5Udp => "socks5_udp", }; let justification = format!("{action_verb} {protocol_label} access to {host}"); ExecPolicyNetworkRuleAmendment { protocol, decision, justification, }}#[cfg(test)]#[path = "network_policy_decision_tests.rs"]mod tests;