Files
codex/codex-rs/core/src/tools/sandboxing_tests.rs
Michael Bolin 0a83353ca3 test: reduce core sandbox policy test setup (#23036)
## Why

`SandboxPolicy` is a legacy compatibility shape, but several core tests
still used it for ordinary turn setup even when the runtime path now
carries `PermissionProfile`. With the first cleanup PR merged, this
follow-up trims more core test scaffolding so remaining `SandboxPolicy`
matches are easier to classify as production compatibility,
legacy-boundary coverage, or explicit conversion tests.

## What Changed

- Updated apply-patch handler and runtime tests to pass
`PermissionProfile` directly.
- Changed sandboxing test helpers to build permission profiles without
first creating `SandboxPolicy` values.
- Converted request-permissions integration turns to pass
`PermissionProfile` through the test helper, leaving legacy sandbox
projection at the `Op::UserTurn` boundary.
- Converted unified exec integration helpers and direct turn submissions
to use `PermissionProfile` values instead of `SandboxPolicy` setup.
- Removed now-unused `SandboxPolicy` imports from the touched core
tests.

## Test Plan

- `just fmt`
- `cargo test -p codex-core --lib tools::sandboxing::tests`
- `cargo test -p codex-core --lib tools::runtimes::apply_patch::tests`
- `cargo test -p codex-core --lib tools::handlers::apply_patch::tests`
- `cargo test -p codex-core --lib unified_exec::process_manager::tests`
- `cargo test -p codex-core --test all request_permissions::`
- `cargo test -p codex-core --test all unified_exec::`
- `just fix -p codex-core`
2026-05-17 08:39:41 -07:00

174 lines
5.3 KiB
Rust

use super::*;
use crate::sandboxing::SandboxPermissions;
use crate::tools::hook_names::HookToolName;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::protocol::GranularApprovalConfig;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn bash_permission_request_payload_omits_missing_description() {
assert_eq!(
PermissionRequestPayload::bash("echo hi".to_string(), /*description*/ None),
PermissionRequestPayload {
tool_name: HookToolName::bash(),
tool_input: json!({ "command": "echo hi" }),
}
);
}
#[test]
fn bash_permission_request_payload_includes_description_when_present() {
assert_eq!(
PermissionRequestPayload::bash(
"echo hi".to_string(),
Some("network-access example.com".to_string()),
),
PermissionRequestPayload {
tool_name: HookToolName::bash(),
tool_input: json!({
"command": "echo hi",
"description": "network-access example.com",
}),
}
);
}
#[test]
fn external_sandbox_skips_exec_approval_on_request() {
assert_eq!(
default_exec_approval_requirement(
AskForApproval::OnRequest,
&FileSystemSandboxPolicy::external_sandbox(),
),
ExecApprovalRequirement::Skip {
bypass_sandbox: false,
proposed_execpolicy_amendment: None,
}
);
}
#[test]
fn restricted_sandbox_requires_exec_approval_on_request() {
assert_eq!(
default_exec_approval_requirement(
AskForApproval::OnRequest,
&FileSystemSandboxPolicy::default()
),
ExecApprovalRequirement::NeedsApproval {
reason: None,
proposed_execpolicy_amendment: None,
}
);
}
#[test]
fn default_exec_approval_requirement_rejects_sandbox_prompt_when_granular_disables_it() {
let policy = AskForApproval::Granular(GranularApprovalConfig {
sandbox_approval: false,
rules: true,
skill_approval: true,
request_permissions: true,
mcp_elicitations: true,
});
let requirement =
default_exec_approval_requirement(policy, &FileSystemSandboxPolicy::default());
assert_eq!(
requirement,
ExecApprovalRequirement::Forbidden {
reason: "approval policy disallowed sandbox approval prompt".to_string(),
}
);
}
#[test]
fn default_exec_approval_requirement_keeps_prompt_when_granular_allows_sandbox_approval() {
let policy = AskForApproval::Granular(GranularApprovalConfig {
sandbox_approval: true,
rules: false,
skill_approval: true,
request_permissions: true,
mcp_elicitations: false,
});
let requirement =
default_exec_approval_requirement(policy, &FileSystemSandboxPolicy::default());
assert_eq!(
requirement,
ExecApprovalRequirement::NeedsApproval {
reason: None,
proposed_execpolicy_amendment: None,
}
);
}
#[test]
fn additional_permissions_allow_bypass_sandbox_first_attempt_when_execpolicy_skips() {
assert_eq!(
sandbox_override_for_first_attempt(
SandboxPermissions::WithAdditionalPermissions,
&ExecApprovalRequirement::Skip {
bypass_sandbox: true,
proposed_execpolicy_amendment: None,
},
&FileSystemSandboxPolicy::default(),
),
SandboxOverride::BypassSandboxFirstAttempt
);
}
#[test]
fn guardian_bypasses_sandbox_for_explicit_escalation_on_first_attempt() {
assert_eq!(
sandbox_override_for_first_attempt(
SandboxPermissions::RequireEscalated,
&ExecApprovalRequirement::Skip {
bypass_sandbox: false,
proposed_execpolicy_amendment: None,
},
&FileSystemSandboxPolicy::default(),
),
SandboxOverride::BypassSandboxFirstAttempt
);
}
#[test]
fn deny_read_blocks_explicit_escalation_but_preserves_policy_bypass() {
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::None,
}]);
assert_eq!(
sandbox_override_for_first_attempt(
SandboxPermissions::RequireEscalated,
&ExecApprovalRequirement::Skip {
bypass_sandbox: false,
proposed_execpolicy_amendment: None,
},
&file_system_policy,
),
SandboxOverride::NoOverride,
"explicit escalation would drop deny-read filesystem policy, so keep the first attempt sandboxed",
);
assert_eq!(
sandbox_override_for_first_attempt(
SandboxPermissions::WithAdditionalPermissions,
&ExecApprovalRequirement::Skip {
bypass_sandbox: true,
proposed_execpolicy_amendment: None,
},
&file_system_policy,
),
SandboxOverride::BypassSandboxFirstAttempt,
"exec-policy allow rules intentionally bypass sandbox even when deny-read entries exist",
);
}