#![allow(clippy::unwrap_used, clippy::expect_used)] use anyhow::Result; use codex_core::protocol::AskForApproval; use codex_core::protocol::EventMsg; use codex_core::protocol::Op; use codex_core::protocol::SandboxPolicy; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::user_input::UserInput; use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_function_call; use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_once; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::test_codex::test_codex; use core_test_support::wait_for_event; use serde_json::json; use std::fs; #[tokio::test] async fn execpolicy_blocks_shell_invocation() -> Result<()> { // TODO execpolicy doesn't parse powershell commands yet if cfg!(windows) { return Ok(()); } let mut builder = test_codex().with_config(|config| { let policy_path = config.codex_home.join("rules").join("policy.rules"); fs::create_dir_all( policy_path .parent() .expect("policy directory must have a parent"), ) .expect("create policy directory"); fs::write( &policy_path, r#"prefix_rule(pattern=["echo"], decision="forbidden")"#, ) .expect("write policy file"); }); let server = start_mock_server().await; let test = builder.build(&server).await?; let call_id = "shell-forbidden"; let args = json!({ "command": "echo blocked", "timeout_ms": 1_000, }); mount_sse_once( &server, sse(vec![ ev_response_created("resp-1"), ev_function_call(call_id, "shell_command", &serde_json::to_string(&args)?), ev_completed("resp-1"), ]), ) .await; mount_sse_once( &server, sse(vec![ ev_assistant_message("msg-1", "done"), ev_completed("resp-2"), ]), ) .await; let session_model = test.session_configured.model.clone(); test.codex .submit(Op::UserTurn { items: vec![UserInput::Text { text: "run shell command".into(), text_elements: Vec::new(), }], final_output_json_schema: None, cwd: test.cwd_path().to_path_buf(), approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::DangerFullAccess, model: session_model, effort: None, summary: ReasoningSummary::Auto, collaboration_mode: None, personality: None, }) .await?; let EventMsg::ExecCommandEnd(end) = wait_for_event(&test.codex, |event| { matches!(event, EventMsg::ExecCommandEnd(_)) }) .await else { unreachable!() }; wait_for_event(&test.codex, |event| { matches!(event, EventMsg::TurnComplete(_)) }) .await; assert!( end.aggregated_output .contains("policy forbids commands starting with `echo`"), "unexpected output: {}", end.aggregated_output ); Ok(()) }