feat: support allowed_sandbox_modes in requirements.toml (#8298)

This adds support for `allowed_sandbox_modes` in `requirements.toml` and
provides legacy support for constraining sandbox modes in
`managed_config.toml`. This is converted to `Constrained<SandboxPolicy>`
in `ConfigRequirements` and applied to `Config` such that constraints
are enforced throughout the harness.

Note that, because `managed_config.toml` is deprecated, we do not add
support for the new `external-sandbox` variant recently introduced in
https://github.com/openai/codex/pull/8290. As noted, that variant is not
supported in `config.toml` today, but can be configured programmatically
via app server.
This commit is contained in:
Michael Bolin
2025-12-19 13:09:20 -08:00
committed by GitHub
parent ec3738b47e
commit dc61fc5f50
25 changed files with 345 additions and 96 deletions

View File

@@ -1464,7 +1464,7 @@ async fn run_scenario(scenario: &ScenarioSpec) -> Result<()> {
let mut builder = test_codex().with_model(model).with_config(move |config| {
config.approval_policy = Constrained::allow_any(approval_policy);
config.sandbox_policy = sandbox_policy.clone();
config.sandbox_policy = Constrained::allow_any(sandbox_policy.clone());
for feature in features {
config.features.enable(feature);
}
@@ -1570,7 +1570,7 @@ async fn approving_execpolicy_amendment_persists_policy_and_skips_future_prompts
let sandbox_policy_for_config = sandbox_policy.clone();
let mut builder = test_codex().with_config(move |config| {
config.approval_policy = Constrained::allow_any(approval_policy);
config.sandbox_policy = sandbox_policy_for_config;
config.sandbox_policy = Constrained::allow_any(sandbox_policy_for_config);
});
let test = builder.build(&server).await?;
let allow_prefix_path = test.cwd.path().join("allow-prefix.txt");

View File

@@ -63,7 +63,7 @@ async fn codex_delegate_forwards_exec_approval_and_proceeds_on_approval() {
// routes ExecApprovalRequest via the parent.
let mut builder = test_codex().with_model("gpt-5.1").with_config(|config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
config.sandbox_policy = SandboxPolicy::ReadOnly;
config.sandbox_policy = Constrained::allow_any(SandboxPolicy::ReadOnly);
});
let test = builder.build(&server).await.expect("build test codex");
@@ -140,7 +140,7 @@ async fn codex_delegate_forwards_patch_approval_and_proceeds_on_decision() {
let mut builder = test_codex().with_model("gpt-5.1").with_config(|config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
// Use a restricted sandbox so patch approval is required
config.sandbox_policy = SandboxPolicy::ReadOnly;
config.sandbox_policy = Constrained::allow_any(SandboxPolicy::ReadOnly);
config.include_apply_patch_tool = true;
});
let test = builder.build(&server).await.expect("build test codex");

View File

@@ -935,7 +935,7 @@ async fn handle_container_exec_autoapprove_from_config_records_tool_decision() {
let TestCodex { codex, .. } = test_codex()
.with_config(|config| {
config.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
config.sandbox_policy = SandboxPolicy::DangerFullAccess;
config.sandbox_policy = Constrained::allow_any(SandboxPolicy::DangerFullAccess);
})
.build(&server)
.await

View File

@@ -605,7 +605,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
let default_cwd = config.cwd.clone();
let default_approval_policy = config.approval_policy.value();
let default_sandbox_policy = config.sandbox_policy.clone();
let default_sandbox_policy = config.sandbox_policy.get();
let default_model = session_configured.model;
let default_effort = config.model_reasoning_effort;
let default_summary = config.model_reasoning_summary;
@@ -695,7 +695,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
let default_cwd = config.cwd.clone();
let default_approval_policy = config.approval_policy.value();
let default_sandbox_policy = config.sandbox_policy.clone();
let default_sandbox_policy = config.sandbox_policy.get();
let default_model = session_configured.model;
let default_effort = config.model_reasoning_effort;
let default_summary = config.model_reasoning_summary;

View File

@@ -24,7 +24,7 @@ fn resume_history(
let turn_ctx = TurnContextItem {
cwd: config.cwd.clone(),
approval_policy: config.approval_policy.value(),
sandbox_policy: config.sandbox_policy.clone(),
sandbox_policy: config.sandbox_policy.get().clone(),
model: previous_model.to_string(),
effort: config.model_reasoning_effort,
summary: config.model_reasoning_summary,

View File

@@ -415,7 +415,10 @@ async fn shell_timeout_handles_background_grandchild_stdout() -> Result<()> {
let server = start_mock_server().await;
let mut builder = test_codex().with_model("gpt-5.1").with_config(|config| {
config.sandbox_policy = SandboxPolicy::DangerFullAccess;
config
.sandbox_policy
.set(SandboxPolicy::DangerFullAccess)
.expect("set sandbox policy");
});
let test = builder.build(&server).await?;
@@ -508,7 +511,9 @@ async fn shell_spawn_failure_truncates_exec_error() -> Result<()> {
let server = start_mock_server().await;
let mut builder = test_codex().with_config(|cfg| {
cfg.sandbox_policy = SandboxPolicy::DangerFullAccess;
cfg.sandbox_policy
.set(SandboxPolicy::DangerFullAccess)
.expect("set sandbox policy");
});
let test = builder.build(&server).await?;