Download codex-rs/exec-server/src/network_policy_decisions_tests.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 7.61 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/exec-server/src/network_policy_decisions_tests.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/exec-server/src/network_policy_decisions_tests.rs
-
curl -L -o network_policy_decisions_tests.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/exec-server/src/network_policy_decisions_tests.rs
7.61 kB
| use std::sync::Arc; | |
| use std::sync::RwLock; | |
| use std::time::Duration; | |
| use codex_network_proxy::NetworkDecision; | |
| use codex_network_proxy::NetworkPolicyRequest; | |
| use codex_network_proxy::NetworkPolicyRequestArgs; | |
| use codex_network_proxy::NetworkProtocol; | |
| use pretty_assertions::assert_eq; | |
| use tokio::sync::mpsc; | |
| use tokio::task::JoinHandle; | |
| use tokio::time::timeout; | |
| use super::*; | |
| use crate::protocol::ExecServerNetworkPolicyDecision; | |
| use crate::protocol::NetworkPolicyRequestParams; | |
| use crate::protocol::NetworkPolicyRequestResponse; | |
| use crate::rpc::RpcServerOutboundMessage; | |
| struct DeciderHarness { | |
| requests: RpcServerRequestSender, | |
| outgoing: mpsc::Receiver<RpcServerOutboundMessage>, | |
| controller_timeout: Duration, | |
| process_shutdown: CancellationToken, | |
| } | |
| impl DeciderHarness { | |
| fn new() -> Self { | |
| let (outgoing_tx, outgoing) = mpsc::channel(/*buffer*/ 8); | |
| Self { | |
| requests: RpcServerRequestSender::new(outgoing_tx), | |
| outgoing, | |
| controller_timeout: Duration::from_secs(60), | |
| process_shutdown: CancellationToken::new(), | |
| } | |
| } | |
| fn request(&self, host: &str) -> JoinHandle<NetworkDecision> { | |
| let decider = network_policy_decider( | |
| ProcessId::from("process"), | |
| Arc::new(RwLock::new(Some(self.requests.clone()))), | |
| self.controller_timeout, | |
| self.process_shutdown.clone(), | |
| ); | |
| let request = NetworkPolicyRequest::new(NetworkPolicyRequestArgs { | |
| protocol: NetworkProtocol::HttpsConnect, | |
| host: host.to_string(), | |
| port: 443, | |
| environment_id: None, | |
| client_addr: None, | |
| method: None, | |
| command: None, | |
| exec_policy_hint: None, | |
| }); | |
| tokio::spawn(async move { decider.decide(request).await }) | |
| } | |
| async fn next_request( | |
| &mut self, | |
| ) -> ( | |
| codex_exec_server_protocol::RequestId, | |
| NetworkPolicyRequestParams, | |
| ) { | |
| let outbound = timeout(Duration::from_secs(1), self.outgoing.recv()) | |
| .await | |
| .expect("policy request should arrive") | |
| .expect("policy request"); | |
| let RpcServerOutboundMessage::Request(request) = outbound else { | |
| panic!("expected policy request"); | |
| }; | |
| assert_eq!(request.method, NETWORK_POLICY_REQUEST_METHOD); | |
| let params = serde_json::from_value(request.params.expect("request params")) | |
| .expect("deserialize policy request"); | |
| (request.id, params) | |
| } | |
| } | |
| async fn await_decision(decision: JoinHandle<NetworkDecision>) -> NetworkDecision { | |
| timeout(Duration::from_secs(1), decision) | |
| .await | |
| .expect("network policy decision should resolve") | |
| .expect("network policy decision task") | |
| } | |
| async fn returns_client_policy_decision() { | |
| let mut harness = DeciderHarness::new(); | |
| let decision = harness.request("example.com"); | |
| let (request_id, params) = harness.next_request().await; | |
| assert_eq!(params.process_id, ProcessId::from("process")); | |
| assert_eq!(params.request.host, "example.com"); | |
| harness.requests.complete( | |
| request_id, | |
| Ok(serde_json::to_value(NetworkPolicyRequestResponse { | |
| decision: ExecServerNetworkPolicyDecision::Allow, | |
| }) | |
| .expect("serialize policy response")), | |
| ); | |
| assert_eq!(await_decision(decision).await, NetworkDecision::Allow); | |
| } | |
| async fn policy_response_reasons_are_bounded_and_fail_closed() { | |
| let mut harness = DeciderHarness::new(); | |
| let boundary_reason = "d".repeat(MAX_NETWORK_POLICY_REASON_BYTES); | |
| let cases = [ | |
| ( | |
| ExecServerNetworkPolicyDecision::Deny { | |
| reason: boundary_reason.clone(), | |
| }, | |
| NetworkDecision::deny(boundary_reason), | |
| ), | |
| ( | |
| ExecServerNetworkPolicyDecision::Deny { | |
| reason: "d".repeat(MAX_NETWORK_POLICY_REASON_BYTES + 1), | |
| }, | |
| NetworkDecision::deny("not_allowed"), | |
| ), | |
| ( | |
| ExecServerNetworkPolicyDecision::Ask { | |
| reason: "ask permission".to_string(), | |
| }, | |
| NetworkDecision::ask("ask permission"), | |
| ), | |
| ( | |
| ExecServerNetworkPolicyDecision::Ask { | |
| reason: "ask\npermission".to_string(), | |
| }, | |
| NetworkDecision::deny("not_allowed"), | |
| ), | |
| ]; | |
| for (response, expected) in cases { | |
| let decision = harness.request("example.com"); | |
| let (request_id, _) = harness.next_request().await; | |
| harness.requests.complete( | |
| request_id, | |
| Ok( | |
| serde_json::to_value(NetworkPolicyRequestResponse { decision: response }) | |
| .expect("serialize policy response"), | |
| ), | |
| ); | |
| assert_eq!(await_decision(decision).await, expected); | |
| } | |
| } | |
| async fn boundary_host_is_relayed() { | |
| let mut harness = DeciderHarness::new(); | |
| let host = "h".repeat(MAX_NETWORK_POLICY_HOST_BYTES); | |
| let decision = harness.request(&host); | |
| let (request_id, params) = harness.next_request().await; | |
| assert_eq!(params.request.host, host); | |
| harness.requests.complete( | |
| request_id, | |
| Ok(serde_json::to_value(NetworkPolicyRequestResponse { | |
| decision: ExecServerNetworkPolicyDecision::Allow, | |
| }) | |
| .expect("serialize policy response")), | |
| ); | |
| assert_eq!(await_decision(decision).await, NetworkDecision::Allow); | |
| } | |
| async fn invalid_hosts_fail_closed_before_reverse_rpc() { | |
| let mut harness = DeciderHarness::new(); | |
| let invalid_hosts = [ | |
| String::new(), | |
| "host name".to_string(), | |
| "host\u{0000}name".to_string(), | |
| "h".repeat(MAX_NETWORK_POLICY_HOST_BYTES + 1), | |
| ]; | |
| for host in invalid_hosts { | |
| assert_eq!( | |
| await_decision(harness.request(&host)).await, | |
| NetworkDecision::deny("not_allowed") | |
| ); | |
| assert!(harness.outgoing.try_recv().is_err()); | |
| assert_eq!(harness.requests.pending_request_count(), 0); | |
| } | |
| } | |
| async fn process_exit_and_disconnect_fail_closed() { | |
| let mut process_exit = DeciderHarness::new(); | |
| let process_decision = process_exit.request("process-exit.example.com"); | |
| process_exit.next_request().await; | |
| process_exit.process_shutdown.cancel(); | |
| assert_eq!( | |
| await_decision(process_decision).await, | |
| NetworkDecision::deny("not_allowed") | |
| ); | |
| assert_eq!(process_exit.requests.pending_request_count(), 0); | |
| let mut disconnect = DeciderHarness::new(); | |
| let disconnect_decision = disconnect.request("disconnect.example.com"); | |
| disconnect.next_request().await; | |
| disconnect.requests.close(); | |
| assert_eq!( | |
| await_decision(disconnect_decision).await, | |
| NetworkDecision::deny("not_allowed") | |
| ); | |
| assert_eq!(disconnect.requests.pending_request_count(), 0); | |
| } | |
| async fn configured_decision_timeout_fails_closed() { | |
| let mut harness = DeciderHarness::new(); | |
| harness.controller_timeout = Duration::from_secs(17); | |
| let decision = harness.request("timeout.example.com"); | |
| harness.next_request().await; | |
| tokio::time::advance(Duration::from_secs(21)).await; | |
| assert!(!decision.is_finished()); | |
| tokio::time::advance(Duration::from_secs(1)).await; | |
| assert_eq!( | |
| await_decision(decision).await, | |
| NetworkDecision::deny("not_allowed") | |
| ); | |
| assert_eq!(harness.requests.pending_request_count(), 0); | |
| } | |