mirror of
https://github.com/openai/codex.git
synced 2026-10-21 05:22:21 +00:00
Read turn permissions from the current configuration (#36930)
## What changed - Remove the cached permission profile from `TurnContext` and derive the effective profile, filesystem policy, network policy, and legacy sandbox policy from `config.permissions`. - Update sandbox consumers and tests to use the current turn configuration. - Verify that role-based agent spawning reapplies runtime permissions and that cold-resumed agents retain their disabled permission profile. GitOrigin-RevId: d67c5d2bcbe6dc76f15b56defb485b155fd1f138
This commit is contained in:
@@ -244,7 +244,7 @@ pub(crate) async fn build_guardian_prompt_items_with_parent_turn(
|
||||
fn parent_turn_denied_reads_context(turn: &TurnContext) -> Option<String> {
|
||||
#[allow(deprecated)]
|
||||
let cwd = &turn.cwd;
|
||||
let file_system_policy = turn.permission_profile.file_system_sandbox_policy();
|
||||
let file_system_policy = turn.file_system_sandbox_policy();
|
||||
let mut entries = file_system_policy
|
||||
.get_unreadable_roots_with_cwd(cwd)
|
||||
.into_iter()
|
||||
|
||||
@@ -467,7 +467,7 @@ async fn build_guardian_prompt_includes_parent_turn_denied_reads() -> anyhow::Re
|
||||
session.thread_id = fixed_guardian_parent_session_id();
|
||||
let denied_root = test_path_buf("/repo/private").abs();
|
||||
let denied_glob = test_path_buf("/repo/private/**").display().to_string();
|
||||
turn.permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
@@ -493,6 +493,10 @@ async fn build_guardian_prompt_includes_parent_turn_denied_reads() -> anyhow::Re
|
||||
]),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
);
|
||||
Arc::make_mut(&mut turn.config)
|
||||
.permissions
|
||||
.set_permission_profile(permission_profile)
|
||||
.expect("test setup should allow updating permission profile");
|
||||
let session = Arc::new(session);
|
||||
let turn = Arc::new(turn);
|
||||
seed_guardian_parent_history(&session, &turn).await;
|
||||
|
||||
@@ -229,7 +229,6 @@ mod tests {
|
||||
|
||||
fn set_primary_environment_cwd(turn_context: &mut TurnContext, cwd: &Path) {
|
||||
let cwd = AbsolutePathBuf::try_from(cwd).expect("absolute path");
|
||||
turn_context.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
|
||||
let TurnEnvironmentState::Ready(primary) = &mut turn_context.environments.environments[0]
|
||||
else {
|
||||
panic!("expected ready primary environment");
|
||||
|
||||
@@ -87,7 +87,7 @@ fn approval_metadata(
|
||||
|
||||
fn approval_config(turn_context: &TurnContext) -> codex_mcp::McpConfig {
|
||||
let mut config = (*mcp_config_for_test(&turn_context.config)).clone();
|
||||
config.permission_profile = turn_context.permission_profile.clone();
|
||||
config.permission_profile = turn_context.permission_profile();
|
||||
config
|
||||
}
|
||||
|
||||
@@ -2814,7 +2814,10 @@ async fn full_access_mode_skips_mcp_tool_approval_for_all_approval_modes() {
|
||||
.approval_policy
|
||||
.set(AskForApproval::Never)
|
||||
.expect("test setup should allow updating approval policy");
|
||||
turn_context.permission_profile = PermissionProfile::Disabled;
|
||||
Arc::make_mut(&mut turn_context.config)
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::Disabled)
|
||||
.expect("test setup should allow updating permission profile");
|
||||
|
||||
let session = Arc::new(session);
|
||||
let turn_context = Arc::new(turn_context);
|
||||
|
||||
@@ -95,7 +95,7 @@ pub(super) async fn spawn_review_thread(
|
||||
review_turn_id.clone(),
|
||||
#[allow(deprecated)]
|
||||
parent_turn_context.cwd.clone(),
|
||||
&parent_turn_context.permission_profile,
|
||||
&parent_turn_context.permission_profile(),
|
||||
parent_turn_context.windows_sandbox_level,
|
||||
parent_turn_context.network.is_some(),
|
||||
));
|
||||
@@ -134,7 +134,6 @@ pub(super) async fn spawn_review_thread(
|
||||
.clone(),
|
||||
multi_agent_version: MultiAgentVersion::Disabled,
|
||||
personality: parent_turn_context.personality,
|
||||
permission_profile: parent_turn_context.permission_profile(),
|
||||
network: parent_turn_context.network.clone(),
|
||||
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
|
||||
#[allow(deprecated)]
|
||||
|
||||
@@ -9246,11 +9246,15 @@ async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy()
|
||||
async fn turn_context_item_stores_split_file_system_sandbox_policy_when_different() {
|
||||
let (_session, mut turn_context) = make_session_and_context().await;
|
||||
let file_system_sandbox_policy = file_system_policy_with_unreadable_glob(&turn_context);
|
||||
turn_context.permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
turn_context.permission_profile.enforcement(),
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
turn_context.permission_profile().enforcement(),
|
||||
&file_system_sandbox_policy,
|
||||
turn_context.network_sandbox_policy(),
|
||||
);
|
||||
Arc::make_mut(&mut turn_context.config)
|
||||
.permissions
|
||||
.set_permission_profile(permission_profile)
|
||||
.expect("test setup should allow updating permission profile");
|
||||
|
||||
let item = turn_context.to_turn_context_item();
|
||||
|
||||
@@ -9409,11 +9413,15 @@ async fn record_context_updates_and_set_reference_context_item_persists_split_fi
|
||||
{
|
||||
let (mut session, mut turn_context) = make_session_and_context().await;
|
||||
let file_system_sandbox_policy = file_system_policy_with_unreadable_glob(&turn_context);
|
||||
turn_context.permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
turn_context.permission_profile.enforcement(),
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
turn_context.permission_profile().enforcement(),
|
||||
&file_system_sandbox_policy,
|
||||
turn_context.network_sandbox_policy(),
|
||||
);
|
||||
Arc::make_mut(&mut turn_context.config)
|
||||
.permissions
|
||||
.set_permission_profile(permission_profile)
|
||||
.expect("test setup should allow updating permission profile");
|
||||
let rollout_path = attach_thread_persistence(&mut session).await;
|
||||
|
||||
let turn_context = Arc::new(turn_context);
|
||||
@@ -11219,7 +11227,10 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() {
|
||||
// command. Force DangerFullAccess so this check stays focused on approval
|
||||
// policy rather than platform-specific sandbox behavior.
|
||||
let turn_context_mut = Arc::get_mut(&mut turn_context).expect("unique thread settings Arc");
|
||||
turn_context_mut.permission_profile = PermissionProfile::Disabled;
|
||||
Arc::make_mut(&mut turn_context_mut.config)
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::Disabled)
|
||||
.expect("test setup should allow updating permission profile");
|
||||
|
||||
let command = session.user_shell().derive_exec_args(
|
||||
command_script,
|
||||
|
||||
@@ -304,7 +304,6 @@ async fn guardian_allows_shell_command_additional_permissions_requests_past_poli
|
||||
.features
|
||||
.enable(Feature::ExecPermissionApprovals)
|
||||
.expect("test setup should allow enabling request permissions");
|
||||
turn_context_raw.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
|
||||
let mut config = (*turn_context_raw.config).clone();
|
||||
config
|
||||
.permissions
|
||||
@@ -420,7 +419,6 @@ async fn strict_auto_review_turn_grant_forces_guardian_for_shell_command_policy_
|
||||
.approval_policy
|
||||
.set(AskForApproval::Never)
|
||||
.expect("test setup should allow updating approval policy");
|
||||
turn_context_raw.permission_profile = codex_protocol::models::PermissionProfile::Disabled;
|
||||
let mut config = (*turn_context_raw.config).clone();
|
||||
config
|
||||
.permissions
|
||||
|
||||
@@ -15,7 +15,6 @@ use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::MultiAgentVersion;
|
||||
use codex_protocol::protocol::ThreadHistoryMode;
|
||||
use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
|
||||
use codex_sandboxing::policy_transforms::effective_permission_profile;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use futures::FutureExt;
|
||||
@@ -136,7 +135,6 @@ pub struct TurnContext {
|
||||
pub(crate) collaboration_mode_developer_instructions: Option<String>,
|
||||
pub(crate) multi_agent_version: MultiAgentVersion,
|
||||
pub(crate) personality: Option<Personality>,
|
||||
pub(crate) permission_profile: PermissionProfile,
|
||||
pub(crate) network: Option<NetworkProxy>,
|
||||
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
|
||||
pub(crate) available_models: Vec<ModelPreset>,
|
||||
@@ -190,23 +188,20 @@ impl TurnContext {
|
||||
}
|
||||
|
||||
pub(crate) fn permission_profile(&self) -> PermissionProfile {
|
||||
self.permission_profile.clone()
|
||||
self.config.permissions.effective_permission_profile()
|
||||
}
|
||||
|
||||
pub(crate) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
|
||||
self.permission_profile.file_system_sandbox_policy()
|
||||
self.config.permissions.file_system_sandbox_policy()
|
||||
}
|
||||
|
||||
pub(crate) fn network_sandbox_policy(&self) -> NetworkSandboxPolicy {
|
||||
self.permission_profile.network_sandbox_policy()
|
||||
self.config.permissions.network_sandbox_policy()
|
||||
}
|
||||
|
||||
pub(crate) fn sandbox_policy(&self) -> SandboxPolicy {
|
||||
compatibility_sandbox_policy_for_permission_profile(
|
||||
&self.permission_profile,
|
||||
#[allow(deprecated)]
|
||||
&self.cwd,
|
||||
)
|
||||
#[allow(deprecated)]
|
||||
self.config.permissions.legacy_sandbox_policy(&self.cwd)
|
||||
}
|
||||
|
||||
pub(crate) fn effective_reasoning_effort(&self) -> Option<ReasoningEffortConfig> {
|
||||
@@ -313,7 +308,6 @@ impl TurnContext {
|
||||
.clone(),
|
||||
multi_agent_version: self.multi_agent_version,
|
||||
personality: self.personality,
|
||||
permission_profile: self.permission_profile.clone(),
|
||||
network: self.network.clone(),
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
available_models,
|
||||
@@ -581,7 +575,6 @@ impl Session {
|
||||
.clone(),
|
||||
multi_agent_version,
|
||||
personality: session_configuration.personality,
|
||||
permission_profile,
|
||||
network,
|
||||
windows_sandbox_level: session_configuration.windows_sandbox_level,
|
||||
available_models,
|
||||
|
||||
@@ -2262,12 +2262,20 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
|
||||
.expect("approval policy should be set");
|
||||
let mut config = (*turn.config).clone();
|
||||
config.approvals_reviewer = ApprovalsReviewer::AutoReview;
|
||||
config
|
||||
.permissions
|
||||
.set_permission_profile(expected_permission_profile.clone())
|
||||
.expect("test setup should allow updating permission profile");
|
||||
set_turn_config(&mut turn, config);
|
||||
turn.permission_profile = expected_permission_profile.clone();
|
||||
let role_name = install_role_with_model_override(&mut turn).await;
|
||||
let mut role_config = (*turn.config).clone();
|
||||
crate::agent::role::apply_role_to_config(&mut role_config, Some(role_name.as_str()))
|
||||
.await
|
||||
.expect("non-empty role config should apply");
|
||||
assert_ne!(
|
||||
role_config.permissions.effective_permission_profile(),
|
||||
expected_permission_profile,
|
||||
turn.config.permissions.effective_permission_profile(),
|
||||
"test requires a runtime profile override that differs from base config"
|
||||
"role config must discard the runtime permission override before it is reapplied"
|
||||
);
|
||||
|
||||
let invocation = invocation(
|
||||
@@ -2276,7 +2284,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
|
||||
"spawn_agent",
|
||||
function_payload(json!({
|
||||
"message": "await this command",
|
||||
"agent_type": "explorer"
|
||||
"agent_type": role_name
|
||||
})),
|
||||
);
|
||||
let output = SpawnAgentHandler::default()
|
||||
@@ -4438,7 +4446,10 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
);
|
||||
turn.permission_profile = permission_profile.clone();
|
||||
Arc::make_mut(&mut turn.config)
|
||||
.permissions
|
||||
.set_permission_profile(permission_profile)
|
||||
.expect("permission profile set");
|
||||
Arc::make_mut(&mut turn.config)
|
||||
.permissions
|
||||
.approval_policy
|
||||
@@ -4464,7 +4475,7 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
|
||||
.expect("approval policy set");
|
||||
expected
|
||||
.permissions
|
||||
.set_permission_profile(permission_profile)
|
||||
.set_permission_profile(turn.permission_profile())
|
||||
.expect("permission profile set");
|
||||
assert_eq!(config, expected);
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ impl ExecCommandHandler {
|
||||
// permissions config below. Consult the configured platform-sandbox requirement before
|
||||
// deciding whether parsing may continue without that base path.
|
||||
let sandbox = SandboxManager::new().select_initial(
|
||||
&turn.permission_profile,
|
||||
&turn.permission_profile(),
|
||||
SandboxablePreference::Auto,
|
||||
turn.windows_sandbox_level,
|
||||
turn.network.is_some(),
|
||||
|
||||
@@ -316,7 +316,10 @@ mod tests {
|
||||
replace_primary_environment_cwd(&mut turn, image_cwd.clone());
|
||||
let image_path = image_cwd.join("image.png");
|
||||
std::fs::write(image_path.as_path(), b"not a real image").expect("write test image");
|
||||
turn.permission_profile = PermissionProfile::read_only();
|
||||
Arc::make_mut(&mut turn.config)
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::read_only())
|
||||
.expect("test setup should allow updating permission profile");
|
||||
let turn = Arc::new(turn);
|
||||
|
||||
let result = ViewImageHandler::default()
|
||||
|
||||
@@ -443,11 +443,12 @@ impl ToolRegistry {
|
||||
let tool_name_flat = flat_tool_name(&tool_name);
|
||||
let call_id_owned = invocation.call_id.clone();
|
||||
let otel = invocation.turn.session_telemetry.clone();
|
||||
let permission_profile = invocation.turn.permission_profile();
|
||||
let base_tool_result_tags = [
|
||||
(
|
||||
"sandbox",
|
||||
permission_profile_sandbox_tag(
|
||||
&invocation.turn.permission_profile,
|
||||
&permission_profile,
|
||||
invocation.turn.windows_sandbox_level,
|
||||
invocation.turn.network.is_some(),
|
||||
),
|
||||
@@ -455,7 +456,7 @@ impl ToolRegistry {
|
||||
(
|
||||
"sandbox_policy",
|
||||
permission_profile_policy_tag(
|
||||
&invocation.turn.permission_profile,
|
||||
&permission_profile,
|
||||
#[allow(deprecated)]
|
||||
invocation.turn.cwd.as_path(),
|
||||
),
|
||||
|
||||
@@ -550,10 +550,13 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
|
||||
Arc::make_mut(&mut turn_context.config)
|
||||
.permissions
|
||||
.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
|
||||
turn_context.permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&read_only_file_system_sandbox_policy(),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
);
|
||||
Arc::make_mut(&mut turn_context.config)
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::from_runtime_permissions(
|
||||
&read_only_file_system_sandbox_policy(),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
))
|
||||
.expect("test setup should allow updating permission profile");
|
||||
let workdir = AbsolutePathBuf::try_from(std::env::current_dir()?)?;
|
||||
let target = std::env::temp_dir().join("execve-hook-short-circuit.txt");
|
||||
let target_str = target.display().to_string();
|
||||
|
||||
@@ -310,6 +310,7 @@ async fn cold_root_resume_restores_agent_identity_and_role_on_followup() -> Resu
|
||||
assert!(initial_child_request.requests().iter().any(|request| {
|
||||
request.body_contains_text(INITIAL_TASK)
|
||||
&& request.body_contains_text(ROLE_DEVELOPER_INSTRUCTIONS)
|
||||
&& request.body_contains_text("<permission_profile type=\"disabled\">")
|
||||
&& !request.body_contains_text(SUBAGENT_DEVELOPER_INSTRUCTIONS)
|
||||
}));
|
||||
let initial_worker_config = worker_thread.config_snapshot().await;
|
||||
@@ -492,6 +493,7 @@ async fn cold_root_resume_restores_agent_identity_and_role_on_followup() -> Resu
|
||||
assert!(followup_child_request.requests().iter().any(|request| {
|
||||
request.body_contains_text(FOLLOWUP_TASK)
|
||||
&& request.body_contains_text(ROLE_DEVELOPER_INSTRUCTIONS)
|
||||
&& request.body_contains_text("<permission_profile type=\"disabled\">")
|
||||
&& !request.body_contains_text(SUBAGENT_DEVELOPER_INSTRUCTIONS)
|
||||
}));
|
||||
let requests = server
|
||||
|
||||
Reference in New Issue
Block a user