Codex Handbook
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;