mirror of
https://github.com/openai/codex.git
synced 2026-11-05 08:28:29 +00:00
Require explicit sandbox permissions with shell justifications (#36350)
## What changed Reject `shell_command` and unified `exec_command` calls that provide a `justification` without also specifying `sandbox_permissions`. The model-visible error tells callers to explicitly request `require_escalated` execution or omit the justification. ## Testing - Add unit coverage for `shell_command` argument validation. - Add integration coverage for both shell tool paths, including verification that rejected unified exec calls do not begin execution. GitOrigin-RevId: 6af39d2c889af492440615eae12085b99a8b5169
This commit is contained in:
1 parent
3d1d26915a
commit
7b38c48da9
7 files changed
+178
-3
No files matched your search
@@ -385,6 +385,78 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn unified_exec_rejects_justification_without_sandbox_permissions() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let mut builder = test_codex().with_model("gpt-5.2").with_config(|config| {
|
||||
config.use_experimental_unified_exec_tool = true;
|
||||
config
|
||||
.features
|
||||
.enable(Feature::UnifiedExec)
|
||||
.expect("test config should allow feature update");
|
||||
});
|
||||
let test = builder.build_with_auto_env(&server).await?;
|
||||
|
||||
let call_id = "uexec-missing-sandbox-permissions";
|
||||
let args = json!({
|
||||
"cmd": "echo should not run",
|
||||
"justification": "Allow this command",
|
||||
});
|
||||
let mock = mount_sse_sequence(
|
||||
&server,
|
||||
vec![
|
||||
sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
ev_function_call(call_id, "exec_command", &serde_json::to_string(&args)?),
|
||||
ev_completed("resp-1"),
|
||||
]),
|
||||
sse(vec![
|
||||
ev_response_created("resp-2"),
|
||||
ev_assistant_message("msg-1", "finished"),
|
||||
ev_completed("resp-2"),
|
||||
]),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
submit_unified_exec_turn(
|
||||
&test,
|
||||
"run the command with escalation",
|
||||
PermissionProfile::Disabled,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut saw_exec_begin = false;
|
||||
wait_for_event(&test.codex, |event| match event {
|
||||
EventMsg::ExecCommandBegin(event) if event.call_id == call_id => {
|
||||
saw_exec_begin = true;
|
||||
false
|
||||
}
|
||||
EventMsg::TurnComplete(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.await;
|
||||
assert!(
|
||||
!saw_exec_begin,
|
||||
"rejected exec_command should not emit ExecCommandBegin"
|
||||
);
|
||||
|
||||
let request = mock
|
||||
.last_request()
|
||||
.expect("model should receive the rejected tool call output");
|
||||
let output_item = request.function_call_output(call_id);
|
||||
let output = extract_output_text(&output_item)
|
||||
.expect("rejected tool call should include model-visible text");
|
||||
assert_eq!(
|
||||
output,
|
||||
"`justification` requires an explicit `sandbox_permissions`; use `sandbox_permissions: \"require_escalated\"` for unsandboxed execution, or omit `justification`."
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn unified_exec_emits_exec_command_begin_event() -> Result<()> {
|
||||
// TODO(anp): Remove after unified-exec fixtures use target-native commands.
|
||||
|
||||
Reference in new issue
Block a user