permissions: make runtime config profile-backed

This commit is contained in:
Michael Bolin
2026-04-24 11:08:18 -07:00
parent c86d16a254
commit 975ced4aed
50 changed files with 945 additions and 530 deletions
@@ -2212,11 +2212,7 @@ impl CodexMessageProcessor {
arg0: None,
};
let (
effective_policy,
effective_file_system_sandbox_policy,
effective_network_sandbox_policy,
) = if let Some(permission_profile) = permission_profile {
let effective_permission_profile = if let Some(permission_profile) = permission_profile {
let permission_profile =
codex_protocol::models::PermissionProfile::from(permission_profile);
let sandbox_policy = match permission_profile.to_legacy_sandbox_policy(&sandbox_cwd) {
@@ -2244,9 +2240,9 @@ impl CodexMessageProcessor {
&mut file_system_sandbox_policy,
&self.config.permissions.file_system_sandbox_policy,
);
(
sandbox_policy,
file_system_sandbox_policy,
codex_protocol::models::PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
)
}
@@ -2267,7 +2263,13 @@ impl CodexMessageProcessor {
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&policy, &sandbox_cwd);
let network_sandbox_policy =
codex_protocol::permissions::NetworkSandboxPolicy::from(&policy);
(policy, file_system_sandbox_policy, network_sandbox_policy)
codex_protocol::models::PermissionProfile::from_runtime_permissions_with_enforcement(
codex_protocol::models::SandboxEnforcement::from_legacy_sandbox_policy(
&policy,
),
&file_system_sandbox_policy,
network_sandbox_policy,
)
}
Err(err) => {
let error = JSONRPCErrorError {
@@ -2280,11 +2282,7 @@ impl CodexMessageProcessor {
}
}
} else {
(
self.config.permissions.sandbox_policy.get().clone(),
self.config.permissions.file_system_sandbox_policy.clone(),
self.config.permissions.network_sandbox_policy,
)
self.config.permissions.permission_profile()
};
let codex_linux_sandbox_exe = self.arg0_paths.codex_linux_sandbox_exe.clone();
@@ -2303,9 +2301,7 @@ impl CodexMessageProcessor {
match codex_core::exec::build_exec_request(
exec_params,
&effective_policy,
&effective_file_system_sandbox_policy,
effective_network_sandbox_policy,
&effective_permission_profile,
&sandbox_cwd,
&codex_linux_sandbox_exe,
use_legacy_landlock,
+7 -10
View File
@@ -710,8 +710,7 @@ mod tests {
use std::collections::HashMap;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::ReadOnlyAccess;
use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -734,9 +733,10 @@ mod tests {
access: ReadOnlyAccess::FullAccess,
network_access: false,
};
let cwd = AbsolutePathBuf::current_dir().expect("current dir");
ExecRequest::new(
vec!["cmd".to_string()],
AbsolutePathBuf::current_dir().expect("current dir"),
cwd,
HashMap::new(),
/*network*/ None,
ExecExpiration::DefaultTimeout,
@@ -744,9 +744,7 @@ mod tests {
SandboxType::WindowsRestrictedToken,
WindowsSandboxLevel::Disabled,
/*windows_sandbox_private_desktop*/ false,
sandbox_policy.clone(),
FileSystemSandboxPolicy::from(&sandbox_policy),
NetworkSandboxPolicy::from(&sandbox_policy),
PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy),
/*arg0*/ None,
)
}
@@ -840,6 +838,7 @@ mod tests {
access: ReadOnlyAccess::FullAccess,
network_access: false,
};
let cwd = AbsolutePathBuf::current_dir().expect("current dir");
manager
.start(StartCommandExecParams {
@@ -848,7 +847,7 @@ mod tests {
process_id: Some("proc-100".to_string()),
exec_request: ExecRequest::new(
vec!["sh".to_string(), "-lc".to_string(), "sleep 30".to_string()],
AbsolutePathBuf::current_dir().expect("current dir"),
cwd.clone(),
HashMap::new(),
/*network*/ None,
ExecExpiration::Cancellation(CancellationToken::new()),
@@ -856,9 +855,7 @@ mod tests {
SandboxType::None,
WindowsSandboxLevel::Disabled,
/*windows_sandbox_private_desktop*/ false,
sandbox_policy.clone(),
FileSystemSandboxPolicy::from(&sandbox_policy),
NetworkSandboxPolicy::from(&sandbox_policy),
PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy),
/*arg0*/ None,
),
started_network_proxy: None,
+54
View File
@@ -5201,6 +5201,11 @@ async fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
model_provider: fixture.openai_provider.clone(),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::Never),
permission_profile: Constrained::allow_any(
PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::new_read_only_policy(),
)
),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
file_system_sandbox_policy: FileSystemSandboxPolicy::from(
&SandboxPolicy::new_read_only_policy(),
@@ -5398,6 +5403,11 @@ async fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
model_provider: fixture.openai_custom_provider.clone(),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::UnlessTrusted),
permission_profile: Constrained::allow_any(
PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::new_read_only_policy(),
),
),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
file_system_sandbox_policy: FileSystemSandboxPolicy::from(
&SandboxPolicy::new_read_only_policy(),
@@ -5549,6 +5559,11 @@ async fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
model_provider: fixture.openai_provider.clone(),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
permission_profile: Constrained::allow_any(
PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::new_read_only_policy(),
),
),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
file_system_sandbox_policy: FileSystemSandboxPolicy::from(
&SandboxPolicy::new_read_only_policy(),
@@ -5685,6 +5700,11 @@ async fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
model_provider: fixture.openai_provider.clone(),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
permission_profile: Constrained::allow_any(
PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::new_read_only_policy(),
),
),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
file_system_sandbox_policy: FileSystemSandboxPolicy::from(
&SandboxPolicy::new_read_only_policy(),
@@ -6511,6 +6531,40 @@ async fn explicit_sandbox_mode_falls_back_when_disallowed_by_requirements() -> s
Ok(())
}
#[tokio::test]
async fn permission_profile_override_falls_back_when_disallowed_by_requirements()
-> std::io::Result<()> {
let codex_home = TempDir::new()?;
let requirements = crate::config_loader::ConfigRequirementsToml {
allowed_sandbox_modes: Some(vec![crate::config_loader::SandboxModeRequirement::ReadOnly]),
..Default::default()
};
let config = ConfigBuilder::without_managed_config_for_tests()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.harness_overrides(ConfigOverrides {
permission_profile: Some(PermissionProfile::Disabled),
..Default::default()
})
.cloud_requirements(CloudRequirementsLoader::new(async move {
Ok(Some(requirements))
}))
.build()
.await?;
let expected_sandbox_policy = SandboxPolicy::new_read_only_policy();
assert_eq!(
*config.permissions.sandbox_policy.get(),
expected_sandbox_policy
);
assert_eq!(
config.permissions.permission_profile(),
PermissionProfile::from_legacy_sandbox_policy(&expected_sandbox_policy)
);
Ok(())
}
#[tokio::test]
async fn requirements_web_search_mode_overrides_danger_full_access_default() -> std::io::Result<()>
{
+97 -5
View File
@@ -114,6 +114,7 @@ pub use codex_config::Constrained;
pub use codex_config::ConstraintError;
pub use codex_config::ConstraintResult;
pub use codex_network_proxy::NetworkProxyAuditMetadata;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
pub use codex_sandboxing::system_bwrap_warning;
pub use managed_features::ManagedFeatures;
pub use network_proxy_spec::NetworkProxySpec;
@@ -190,13 +191,25 @@ pub(crate) async fn test_config() -> Config {
pub struct Permissions {
/// Approval policy for executing commands.
pub approval_policy: Constrained<AskForApproval>,
/// Canonical effective runtime permissions after config requirements and
/// runtime readable-root additions have been applied.
pub permission_profile: Constrained<PermissionProfile>,
/// Effective sandbox policy used for shell/unified exec.
///
/// Legacy projection retained while runtime call sites migrate to
/// `permission_profile`.
pub sandbox_policy: Constrained<SandboxPolicy>,
/// Effective filesystem sandbox policy, including entries that cannot yet
/// be fully represented by the legacy [`SandboxPolicy`] projection.
///
/// Runtime projection retained while callers migrate to
/// `permission_profile`.
pub file_system_sandbox_policy: FileSystemSandboxPolicy,
/// Effective network sandbox policy split out from the legacy
/// [`SandboxPolicy`] projection.
///
/// Runtime projection retained while callers migrate to
/// `permission_profile`.
pub network_sandbox_policy: NetworkSandboxPolicy,
/// Effective network configuration applied to all spawned processes.
pub network: Option<NetworkProxySpec>,
@@ -222,12 +235,79 @@ impl Permissions {
/// Effective runtime permissions after config requirements and runtime
/// readable-root additions have been applied.
pub fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(self.sandbox_policy.get()),
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
self.permission_profile.get().clone()
}
/// Replace permissions from a legacy sandbox policy and keep every
/// permission projection in sync.
pub fn set_legacy_sandbox_policy(
&mut self,
sandbox_policy: SandboxPolicy,
cwd: &Path,
) -> ConstraintResult<()> {
self.sandbox_policy.can_set(&sandbox_policy)?;
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, cwd);
let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy);
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
self.permission_profile.can_set(&permission_profile)?;
self.sandbox_policy.set(sandbox_policy)?;
self.permission_profile.set(permission_profile)?;
self.file_system_sandbox_policy = file_system_sandbox_policy;
self.network_sandbox_policy = network_sandbox_policy;
Ok(())
}
/// Replace permissions from the canonical profile and update compatibility
/// projections for legacy consumers.
pub fn set_permission_profile(
&mut self,
permission_profile: PermissionProfile,
cwd: &Path,
) -> ConstraintResult<()> {
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let sandbox_policy = compatibility_sandbox_policy_for_permission_profile(
&permission_profile,
&file_system_sandbox_policy,
network_sandbox_policy,
cwd,
);
self.permission_profile.can_set(&permission_profile)?;
self.sandbox_policy.can_set(&sandbox_policy)?;
self.permission_profile.set(permission_profile)?;
self.sandbox_policy.set(sandbox_policy)?;
self.file_system_sandbox_policy = file_system_sandbox_policy;
self.network_sandbox_policy = network_sandbox_policy;
Ok(())
}
}
fn constrained_permission_profile_from_sandbox_projection(
initial_value: PermissionProfile,
sandbox_constraint: Constrained<SandboxPolicy>,
cwd: AbsolutePathBuf,
) -> std::io::Result<Constrained<PermissionProfile>> {
Constrained::new(initial_value, move |candidate| {
let sandbox_policy = candidate.to_legacy_sandbox_policy(cwd.as_path()).map_err(|err| {
ConstraintError::InvalidValue {
field_name: "permission_profile",
candidate: format!("{candidate:?}"),
allowed: format!(
"permission profiles that can be represented by the active sandbox constraints: {err}"
),
requirement_source: codex_config::RequirementSource::Unknown,
}
})?;
sandbox_constraint.can_set(&sandbox_policy)
})
.map_err(std::io::Error::from)
}
/// Application configuration loaded from disk and merged with overrides.
@@ -2297,6 +2377,17 @@ impl Config {
} else {
NetworkSandboxPolicy::from(&effective_sandbox_policy)
};
let effective_permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&effective_sandbox_policy),
&effective_file_system_sandbox_policy,
effective_network_sandbox_policy,
);
let constrained_permission_profile =
constrained_permission_profile_from_sandbox_projection(
effective_permission_profile,
constrained_sandbox_policy.value.clone(),
resolved_cwd.clone(),
)?;
let config = Self {
model,
service_tier,
@@ -2309,6 +2400,7 @@ impl Config {
startup_warnings,
permissions: Permissions {
approval_policy: constrained_approval_policy.value,
permission_profile: constrained_permission_profile,
sandbox_policy: constrained_sandbox_policy.value,
file_system_sandbox_policy: effective_file_system_sandbox_policy,
network_sandbox_policy: effective_network_sandbox_policy,
@@ -2,7 +2,9 @@ use super::ContextualUserFragment;
use codex_execpolicy::Policy;
use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::config_types::SandboxMode;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::format_allow_prefixes;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::GranularApprovalConfig;
use codex_protocol::protocol::NetworkAccess;
@@ -57,7 +59,33 @@ pub struct PermissionsInstructions {
}
impl PermissionsInstructions {
/// Builds permissions instructions from the effective sandbox and approval policy.
/// Builds permissions instructions from the effective permission profile and approval policy.
pub fn from_permission_profile(
permission_profile: &PermissionProfile,
approval_policy: AskForApproval,
approvals_reviewer: ApprovalsReviewer,
exec_policy: &Policy,
cwd: &Path,
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
) -> Self {
let (sandbox_mode, writable_roots) = sandbox_prompt_from_profile(permission_profile, cwd);
Self::from_permissions_with_network(
sandbox_mode,
network_access_from_policy(permission_profile.network_sandbox_policy()),
PermissionsPromptConfig {
approval_policy,
approvals_reviewer,
exec_policy,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
},
writable_roots,
)
}
/// Builds permissions instructions from a legacy sandbox policy.
pub fn from_policy(
sandbox_policy: &SandboxPolicy,
approval_policy: AskForApproval,
@@ -67,33 +95,14 @@ impl PermissionsInstructions {
exec_permission_approvals_enabled: bool,
request_permissions_tool_enabled: bool,
) -> Self {
let network_access = if sandbox_policy.has_full_network_access() {
NetworkAccess::Enabled
} else {
NetworkAccess::Restricted
};
let (sandbox_mode, writable_roots) = match sandbox_policy {
SandboxPolicy::DangerFullAccess => (SandboxMode::DangerFullAccess, None),
SandboxPolicy::ReadOnly { .. } => (SandboxMode::ReadOnly, None),
SandboxPolicy::ExternalSandbox { .. } => (SandboxMode::DangerFullAccess, None),
SandboxPolicy::WorkspaceWrite { .. } => {
let roots = sandbox_policy.get_writable_roots_with_cwd(cwd);
(SandboxMode::WorkspaceWrite, Some(roots))
}
};
Self::from_permissions_with_network(
sandbox_mode,
network_access,
PermissionsPromptConfig {
approval_policy,
approvals_reviewer,
exec_policy,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
},
writable_roots,
Self::from_permission_profile(
&PermissionProfile::from_legacy_sandbox_policy(sandbox_policy),
approval_policy,
approvals_reviewer,
exec_policy,
cwd,
exec_permission_approvals_enabled,
request_permissions_tool_enabled,
)
}
@@ -125,6 +134,38 @@ impl PermissionsInstructions {
}
}
fn sandbox_prompt_from_profile(
permission_profile: &PermissionProfile,
cwd: &Path,
) -> (SandboxMode, Option<Vec<WritableRoot>>) {
match permission_profile {
PermissionProfile::Disabled | PermissionProfile::External { .. } => {
(SandboxMode::DangerFullAccess, None)
}
PermissionProfile::Managed { .. } => {
let file_system_policy = permission_profile.file_system_sandbox_policy();
if file_system_policy.has_full_disk_write_access() {
return (SandboxMode::DangerFullAccess, None);
}
let writable_roots = file_system_policy.get_writable_roots_with_cwd(cwd);
if writable_roots.is_empty() {
(SandboxMode::ReadOnly, None)
} else {
(SandboxMode::WorkspaceWrite, Some(writable_roots))
}
}
}
}
fn network_access_from_policy(network_policy: NetworkSandboxPolicy) -> NetworkAccess {
if network_policy.is_enabled() {
NetworkAccess::Enabled
} else {
NetworkAccess::Restricted
}
}
impl ContextualUserFragment for PermissionsInstructions {
const ROLE: &'static str = "developer";
const START_MARKER: &'static str = "<permissions instructions>";
@@ -1,5 +1,11 @@
use super::*;
use codex_execpolicy::Decision;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use std::path::PathBuf;
@@ -71,6 +77,36 @@ fn builds_permissions_from_policy() {
assert!(text.contains("`approval_policy` is `unless-trusted`"));
}
#[test]
fn builds_permissions_from_profile() {
let cwd = PathBuf::from("/tmp");
let writable_root =
AbsolutePathBuf::from_absolute_path(cwd.join("repo")).expect("absolute path");
let permission_profile = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: writable_root.clone(),
},
access: FileSystemAccessMode::Write,
}]),
NetworkSandboxPolicy::Enabled,
);
let instructions = PermissionsInstructions::from_permission_profile(
&permission_profile,
AskForApproval::UnlessTrusted,
ApprovalsReviewer::User,
&Policy::empty(),
&cwd,
/*exec_permission_approvals_enabled*/ false,
/*request_permissions_tool_enabled*/ false,
);
let text = instructions.body();
assert!(text.contains("`sandbox_mode` is `workspace-write`"));
assert!(text.contains("Network access is enabled."));
assert!(text.contains(writable_root.to_string_lossy().as_ref()));
}
#[test]
fn includes_request_rule_instructions_for_on_request() {
let mut exec_policy = Policy::empty();
+2 -2
View File
@@ -56,8 +56,8 @@ fn build_permissions_update_item(
}
Some(
PermissionsInstructions::from_policy(
next.sandbox_policy.get(),
PermissionsInstructions::from_permission_profile(
&next.permission_profile,
next.approval_policy.value(),
next.config.approvals_reviewer,
exec_policy,
+9 -13
View File
@@ -30,6 +30,7 @@ use codex_protocol::error::Result;
use codex_protocol::error::SandboxErr;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::exec_output::StreamOutput;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
@@ -218,9 +219,7 @@ pub struct StdoutStream {
#[allow(clippy::too_many_arguments)]
pub async fn process_exec_tool_call(
params: ExecParams,
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: &PermissionProfile,
sandbox_cwd: &AbsolutePathBuf,
codex_linux_sandbox_exe: &Option<PathBuf>,
use_legacy_landlock: bool,
@@ -228,9 +227,7 @@ pub async fn process_exec_tool_call(
) -> Result<ExecToolCallOutput> {
let exec_req = build_exec_request(
params,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
permission_profile,
sandbox_cwd,
codex_linux_sandbox_exe,
use_legacy_landlock,
@@ -244,9 +241,7 @@ pub async fn process_exec_tool_call(
/// spawned under the requested sandbox policy.
pub fn build_exec_request(
params: ExecParams,
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: &PermissionProfile,
sandbox_cwd: &AbsolutePathBuf,
codex_linux_sandbox_exe: &Option<PathBuf>,
use_legacy_landlock: bool,
@@ -269,8 +264,10 @@ pub fn build_exec_request(
} = params;
let enforce_managed_network = network.is_some();
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let sandbox_type = select_process_exec_tool_sandbox_type(
file_system_sandbox_policy,
&file_system_sandbox_policy,
network_sandbox_policy,
windows_sandbox_level,
enforce_managed_network,
@@ -302,9 +299,7 @@ pub fn build_exec_request(
let mut exec_req = manager
.transform(SandboxTransformRequest {
command,
policy: sandbox_policy,
file_system_policy: file_system_sandbox_policy,
network_policy: network_sandbox_policy,
permissions: permission_profile,
sandbox: sandbox_type,
enforce_managed_network,
network: network.as_ref(),
@@ -364,6 +359,7 @@ pub(crate) async fn execute_exec_request(
windows_sandbox_policy_cwd: _,
windows_sandbox_level,
windows_sandbox_private_desktop,
permission_profile: _,
sandbox_policy,
// TODO(mbolin): Use file_system_sandbox_policy instead of sandbox_policy.
file_system_sandbox_policy: _,
+6 -6
View File
@@ -1,5 +1,6 @@
use super::*;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_sandboxing::SandboxType;
use core_test_support::PathBufExt;
use core_test_support::PathExt;
@@ -346,6 +347,7 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result
let cwd = codex_utils_absolute_path::AbsolutePathBuf::current_dir()?;
let sandbox_policy = SandboxPolicy::DangerFullAccess;
let permission_profile = PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy);
let output = process_exec_tool_call(
ExecParams {
command,
@@ -360,9 +362,7 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result
justification: None,
arg0: None,
},
&sandbox_policy,
&FileSystemSandboxPolicy::from(&sandbox_policy),
NetworkSandboxPolicy::Enabled,
&permission_profile,
&cwd,
&None,
/*use_legacy_landlock*/ false,
@@ -1026,11 +1026,11 @@ async fn process_exec_tool_call_respects_cancellation_token() -> Result<()> {
tokio::time::sleep(Duration::from_millis(1_000)).await;
cancel_tx.cancel();
});
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy(&SandboxPolicy::DangerFullAccess);
let result = process_exec_tool_call(
params,
&SandboxPolicy::DangerFullAccess,
&FileSystemSandboxPolicy::from(&SandboxPolicy::DangerFullAccess),
NetworkSandboxPolicy::Enabled,
&permission_profile,
&cwd,
&None,
/*use_legacy_landlock*/ false,
+12 -2
View File
@@ -9,6 +9,7 @@ use codex_analytics::GuardianReviewAnalyticsResult;
use codex_analytics::GuardianReviewSessionKind;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
use codex_protocol::protocol::AskForApproval;
@@ -847,8 +848,17 @@ pub(crate) fn build_guardian_review_session_config(
);
guardian_config.developer_instructions = None;
guardian_config.permissions.approval_policy = Constrained::allow_only(AskForApproval::Never);
guardian_config.permissions.sandbox_policy =
Constrained::allow_only(SandboxPolicy::new_read_only_policy());
let sandbox_policy = SandboxPolicy::new_read_only_policy();
guardian_config.permissions.permission_profile = Constrained::allow_only(
PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy),
);
guardian_config.permissions.sandbox_policy = Constrained::allow_only(sandbox_policy.clone());
guardian_config
.permissions
.set_legacy_sandbox_policy(sandbox_policy, guardian_config.cwd.as_path())
.map_err(|err| {
anyhow::anyhow!("guardian review session could not set sandbox policy: {err}")
})?;
guardian_config.include_apps_instructions = false;
guardian_config
.mcp_servers
+2 -2
View File
@@ -9,7 +9,7 @@ use codex_mcp::ToolInfo;
use codex_models_manager::test_support::construct_model_info_offline_for_tests;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::SessionSource;
use codex_tools::ToolsConfig;
use codex_tools::ToolsConfigParams;
@@ -104,7 +104,7 @@ async fn tools_config_for_mcp_tool_exposure(search_tool: bool) -> ToolsConfig {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
tools_config.search_tool = search_tool;
+1 -14
View File
@@ -16,8 +16,6 @@ use crate::session::session::Session;
use codex_config::Constrained;
use codex_features::Feature;
use codex_protocol::ThreadId;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
@@ -320,21 +318,10 @@ mod agent {
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let consolidation_file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
&consolidation_sandbox_policy,
agent_config.cwd.as_path(),
);
let consolidation_network_sandbox_policy =
NetworkSandboxPolicy::from(&consolidation_sandbox_policy);
agent_config
.permissions
.sandbox_policy
.set(consolidation_sandbox_policy)
.set_legacy_sandbox_policy(consolidation_sandbox_policy, agent_config.cwd.as_path())
.ok()?;
agent_config.permissions.file_system_sandbox_policy =
consolidation_file_system_sandbox_policy;
agent_config.permissions.network_sandbox_policy = consolidation_network_sandbox_policy;
agent_config.model = Some(
config
+39 -2
View File
@@ -1,16 +1,30 @@
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
#[cfg(test)]
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxType;
use codex_sandboxing::get_platform_sandbox;
use std::path::Path;
#[cfg(test)]
pub(crate) fn sandbox_tag(
policy: &SandboxPolicy,
windows_sandbox_level: WindowsSandboxLevel,
) -> &'static str {
if matches!(policy, SandboxPolicy::DangerFullAccess) {
permission_profile_sandbox_tag(
&PermissionProfile::from_legacy_sandbox_policy(policy),
windows_sandbox_level,
)
}
pub(crate) fn permission_profile_sandbox_tag(
profile: &PermissionProfile,
windows_sandbox_level: WindowsSandboxLevel,
) -> &'static str {
if matches!(profile, PermissionProfile::Disabled) {
return "none";
}
if matches!(policy, SandboxPolicy::ExternalSandbox { .. }) {
if matches!(profile, PermissionProfile::External { .. }) {
return "external";
}
if cfg!(target_os = "windows") && matches!(windows_sandbox_level, WindowsSandboxLevel::Elevated)
@@ -23,6 +37,29 @@ pub(crate) fn sandbox_tag(
.unwrap_or("none")
}
pub(crate) fn permission_profile_policy_tag(
profile: &PermissionProfile,
cwd: &Path,
) -> &'static str {
match profile {
PermissionProfile::Disabled => "danger-full-access",
PermissionProfile::External { .. } => "external-sandbox",
PermissionProfile::Managed { .. } => {
let file_system_policy = profile.file_system_sandbox_policy();
if file_system_policy.has_full_disk_write_access() {
"danger-full-access"
} else if file_system_policy
.get_writable_roots_with_cwd(cwd)
.is_empty()
{
"read-only"
} else {
"workspace-write"
}
}
}
}
#[cfg(test)]
#[path = "sandbox_tags_tests.rs"]
mod tests;
+53
View File
@@ -1,10 +1,21 @@
use super::permission_profile_policy_tag;
use super::permission_profile_sandbox_tag;
use super::sandbox_tag;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::NetworkAccess;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxType;
use codex_sandboxing::get_platform_sandbox;
use codex_utils_absolute_path::AbsolutePathBuf;
use pretty_assertions::assert_eq;
use std::path::Path;
#[test]
fn danger_full_access_is_untagged_even_when_linux_sandbox_defaults_apply() {
@@ -37,3 +48,45 @@ fn default_linux_sandbox_uses_platform_sandbox_tag() {
.unwrap_or("none");
assert_eq!(actual, expected);
}
#[test]
fn profile_sandbox_tag_distinguishes_disabled_from_external() {
assert_eq!(
permission_profile_sandbox_tag(&PermissionProfile::Disabled, WindowsSandboxLevel::Disabled),
"none"
);
assert_eq!(
permission_profile_sandbox_tag(
&PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
},
WindowsSandboxLevel::Disabled,
),
"external"
);
}
#[test]
fn profile_policy_tag_reports_closest_legacy_mode() {
let cwd = AbsolutePathBuf::from_absolute_path(Path::new("/tmp/codex")).expect("absolute cwd");
let writable_root = AbsolutePathBuf::from_absolute_path(Path::new("/tmp/codex/work"))
.expect("absolute writable root");
let profile = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy {
kind: FileSystemSandboxKind::Restricted,
glob_scan_max_depth: None,
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: writable_root,
},
access: FileSystemAccessMode::Write,
}],
},
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
permission_profile_policy_tag(&profile, cwd.as_path()),
"workspace-write"
);
}
+15 -3
View File
@@ -18,12 +18,14 @@ use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_network_proxy::NetworkProxy;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::models::PermissionProfile;
pub use codex_protocol::models::SandboxPermissions;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxExecRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap;
@@ -52,6 +54,7 @@ pub struct ExecRequest {
pub windows_sandbox_policy_cwd: AbsolutePathBuf,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
pub permission_profile: PermissionProfile,
pub sandbox_policy: SandboxPolicy,
pub file_system_sandbox_policy: FileSystemSandboxPolicy,
pub network_sandbox_policy: NetworkSandboxPolicy,
@@ -71,12 +74,18 @@ impl ExecRequest {
sandbox: SandboxType,
windows_sandbox_level: WindowsSandboxLevel,
windows_sandbox_private_desktop: bool,
sandbox_policy: SandboxPolicy,
file_system_sandbox_policy: FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: PermissionProfile,
arg0: Option<String>,
) -> Self {
let windows_sandbox_policy_cwd = cwd.clone();
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let sandbox_policy = compatibility_sandbox_policy_for_permission_profile(
&permission_profile,
&file_system_sandbox_policy,
network_sandbox_policy,
cwd.as_path(),
);
Self {
command,
cwd,
@@ -89,6 +98,7 @@ impl ExecRequest {
windows_sandbox_policy_cwd,
windows_sandbox_level,
windows_sandbox_private_desktop,
permission_profile,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
@@ -110,6 +120,7 @@ impl ExecRequest {
sandbox,
windows_sandbox_level,
windows_sandbox_private_desktop,
permission_profile,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
@@ -141,6 +152,7 @@ impl ExecRequest {
windows_sandbox_policy_cwd,
windows_sandbox_level,
windows_sandbox_private_desktop,
permission_profile,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
+3 -2
View File
@@ -634,6 +634,7 @@ impl Codex {
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -2548,8 +2549,8 @@ impl Session {
}
if turn_context.config.include_permissions_instructions {
developer_sections.push(
PermissionsInstructions::from_policy(
turn_context.sandbox_policy.get(),
PermissionsInstructions::from_permission_profile(
&turn_context.permission_profile,
turn_context.approval_policy.value(),
turn_context.config.approvals_reviewer,
self.services.exec_policy.current().as_ref(),
+3 -2
View File
@@ -37,7 +37,7 @@ pub(super) async fn spawn_review_thread(
)),
web_search_mode: Some(review_web_search_mode),
session_source: parent_turn_context.session_source.clone(),
sandbox_policy: parent_turn_context.sandbox_policy.get(),
permission_profile: &parent_turn_context.permission_profile,
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
})
.with_unified_exec_shell_mode_for_session(
@@ -94,7 +94,7 @@ pub(super) async fn spawn_review_thread(
&session_source,
review_turn_id.clone(),
parent_turn_context.cwd.clone(),
parent_turn_context.sandbox_policy.get(),
&parent_turn_context.permission_profile,
parent_turn_context.windows_sandbox_level,
));
@@ -124,6 +124,7 @@ pub(super) async fn spawn_review_thread(
collaboration_mode: parent_turn_context.collaboration_mode.clone(),
personality: parent_turn_context.personality,
approval_policy: parent_turn_context.approval_policy.clone(),
permission_profile: parent_turn_context.permission_profile(),
sandbox_policy: parent_turn_context.sandbox_policy.clone(),
file_system_sandbox_policy: parent_turn_context.file_system_sandbox_policy.clone(),
network_sandbox_policy: parent_turn_context.network_sandbox_policy,
+63 -23
View File
@@ -56,9 +56,13 @@ pub(crate) struct SessionConfiguration {
/// When to escalate for approval for execution
pub(super) approval_policy: Constrained<AskForApproval>,
pub(super) approvals_reviewer: ApprovalsReviewer,
/// How to sandbox commands executed in the system
/// Canonical permission profile for the session.
pub(super) permission_profile: Constrained<PermissionProfile>,
/// Legacy sandbox projection retained while lower-level callers migrate.
pub(super) sandbox_policy: Constrained<SandboxPolicy>,
/// Filesystem sandbox projection of `permission_profile`.
pub(super) file_system_sandbox_policy: FileSystemSandboxPolicy,
/// Network sandbox projection of `permission_profile`.
pub(super) network_sandbox_policy: NetworkSandboxPolicy,
pub(super) windows_sandbox_level: WindowsSandboxLevel,
@@ -94,11 +98,7 @@ impl SessionConfiguration {
}
pub(super) fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(self.sandbox_policy.get()),
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
self.permission_profile.get().clone()
}
pub(super) fn thread_config_snapshot(&self) -> ThreadConfigSnapshot {
@@ -170,23 +170,10 @@ impl SessionConfiguration {
}
if let Some(permission_profile) = updates.permission_profile.clone() {
let sandbox_policy = permission_profile
.to_legacy_sandbox_policy(&next_configuration.cwd)
.map_err(|err| ConstraintError::InvalidValue {
field_name: "permission_profile",
candidate: format!("{permission_profile:?}"),
allowed: format!(
"permission profiles that can be represented by the active sandbox constraints: {err}"
),
requirement_source: codex_config::RequirementSource::Unknown,
})?;
next_configuration.sandbox_policy.set(sandbox_policy)?;
let (mut file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
file_system_sandbox_policy
.preserve_deny_read_restrictions_from(&self.file_system_sandbox_policy);
next_configuration.file_system_sandbox_policy = file_system_sandbox_policy;
next_configuration.network_sandbox_policy = network_sandbox_policy;
next_configuration.set_permission_profile_projection(
permission_profile,
Some(&self.file_system_sandbox_policy),
)?;
} else if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
next_configuration.sandbox_policy.set(sandbox_policy)?;
next_configuration.file_system_sandbox_policy =
@@ -197,6 +184,15 @@ impl SessionConfiguration {
);
next_configuration.network_sandbox_policy =
NetworkSandboxPolicy::from(next_configuration.sandbox_policy.get());
next_configuration.permission_profile.set(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(
next_configuration.sandbox_policy.get(),
),
&next_configuration.file_system_sandbox_policy,
next_configuration.network_sandbox_policy,
),
)?;
} else if cwd_changed && file_system_policy_matches_legacy {
// Preserve richer split policies across cwd-only updates; only
// rederive when the session is already using the legacy bridge.
@@ -205,6 +201,15 @@ impl SessionConfiguration {
next_configuration.sandbox_policy.get(),
&next_configuration.cwd,
);
next_configuration.permission_profile.set(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(
next_configuration.sandbox_policy.get(),
),
&next_configuration.file_system_sandbox_policy,
next_configuration.network_sandbox_policy,
),
)?;
}
if let Some(app_server_client_name) = updates.app_server_client_name.clone() {
next_configuration.app_server_client_name = Some(app_server_client_name);
@@ -214,6 +219,41 @@ impl SessionConfiguration {
}
Ok(next_configuration)
}
fn set_permission_profile_projection(
&mut self,
permission_profile: PermissionProfile,
preserve_deny_reads_from: Option<&FileSystemSandboxPolicy>,
) -> ConstraintResult<()> {
let enforcement = permission_profile.enforcement();
let (mut file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
if let Some(existing_file_system_policy) = preserve_deny_reads_from {
file_system_sandbox_policy
.preserve_deny_read_restrictions_from(existing_file_system_policy);
}
let effective_permission_profile =
PermissionProfile::from_runtime_permissions_with_enforcement(
enforcement,
&file_system_sandbox_policy,
network_sandbox_policy,
);
let sandbox_policy = effective_permission_profile
.to_legacy_sandbox_policy(&self.cwd)
.map_err(|err| ConstraintError::InvalidValue {
field_name: "permission_profile",
candidate: format!("{effective_permission_profile:?}"),
allowed: format!(
"permission profiles that can be represented by the active sandbox constraints: {err}"
),
requirement_source: codex_config::RequirementSource::Unknown,
})?;
self.permission_profile.set(effective_permission_profile)?;
self.sandbox_policy.set(sandbox_policy)?;
self.file_system_sandbox_policy = file_system_sandbox_policy;
self.network_sandbox_policy = network_sandbox_policy;
Ok(())
}
}
#[derive(Default, Clone)]
+13
View File
@@ -37,6 +37,7 @@ use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::FunctionCallOutputBody;
use codex_protocol::models::FunctionCallOutputPayload;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -1485,6 +1486,11 @@ async fn session_configured_reports_permission_profile_for_external_sandbox() ->
config.permissions.sandbox_policy = codex_config::Constrained::allow_any(sandbox_policy);
config.permissions.file_system_sandbox_policy = FileSystemSandboxPolicy::external_sandbox();
config.permissions.network_sandbox_policy = NetworkSandboxPolicy::Restricted;
config.permissions.permission_profile =
codex_config::Constrained::allow_any(PermissionProfile::from_runtime_permissions(
&config.permissions.file_system_sandbox_policy,
config.permissions.network_sandbox_policy,
));
});
let test = builder.build(&server).await?;
@@ -2238,6 +2244,7 @@ async fn set_rate_limits_retains_previous_credits() {
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -2342,6 +2349,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -2791,6 +2799,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -3118,6 +3127,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -3224,6 +3234,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -3444,6 +3455,7 @@ async fn make_session_with_config_and_rx(
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
@@ -4587,6 +4599,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.permissions.approval_policy.clone(),
approvals_reviewer: config.approvals_reviewer,
permission_profile: config.permissions.permission_profile.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
network_sandbox_policy: config.permissions.network_sandbox_policy,
+15 -10
View File
@@ -2,7 +2,6 @@ use super::*;
use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
@@ -73,6 +72,7 @@ pub(crate) struct TurnContext {
pub(crate) collaboration_mode: CollaborationMode,
pub(crate) personality: Option<Personality>,
pub(crate) approval_policy: Constrained<AskForApproval>,
pub(crate) permission_profile: PermissionProfile,
pub(crate) sandbox_policy: Constrained<SandboxPolicy>,
pub(crate) file_system_sandbox_policy: FileSystemSandboxPolicy,
pub(crate) network_sandbox_policy: NetworkSandboxPolicy,
@@ -97,11 +97,7 @@ pub(crate) struct TurnContext {
}
impl TurnContext {
pub(crate) fn permission_profile(&self) -> PermissionProfile {
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&self.sandbox_policy),
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
)
self.permission_profile.clone()
}
pub(crate) fn model_context_window(&self) -> Option<i64> {
@@ -171,7 +167,7 @@ impl TurnContext {
),
web_search_mode: self.tools_config.web_search_mode,
session_source: self.session_source.clone(),
sandbox_policy: self.sandbox_policy.get(),
permission_profile: &self.permission_profile,
windows_sandbox_level: self.windows_sandbox_level,
})
.with_unified_exec_shell_mode(self.tools_config.unified_exec_shell_mode.clone())
@@ -212,6 +208,7 @@ impl TurnContext {
collaboration_mode,
personality: self.personality,
approval_policy: self.approval_policy.clone(),
permission_profile: self.permission_profile.clone(),
sandbox_policy: self.sandbox_policy.clone(),
file_system_sandbox_policy: self.file_system_sandbox_policy.clone(),
network_sandbox_policy: self.network_sandbox_policy,
@@ -258,7 +255,7 @@ impl TurnContext {
additional_permissions.as_ref(),
);
let permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&self.sandbox_policy),
self.permission_profile.enforcement(),
&file_system_sandbox_policy,
network_sandbox_policy,
);
@@ -367,6 +364,13 @@ impl Session {
per_turn_config.service_tier = session_configuration.service_tier;
per_turn_config.personality = session_configuration.personality;
per_turn_config.approvals_reviewer = session_configuration.approvals_reviewer;
per_turn_config.permissions.permission_profile =
session_configuration.permission_profile.clone();
per_turn_config.permissions.sandbox_policy = session_configuration.sandbox_policy.clone();
per_turn_config.permissions.file_system_sandbox_policy =
session_configuration.file_system_sandbox_policy.clone();
per_turn_config.permissions.network_sandbox_policy =
session_configuration.network_sandbox_policy;
let resolved_web_search_mode = resolve_web_search_mode_for_turn(
&per_turn_config.web_search_mode,
session_configuration.sandbox_policy.get(),
@@ -429,7 +433,7 @@ impl Session {
image_generation_tool_auth_allowed,
web_search_mode: Some(per_turn_config.web_search_mode.value()),
session_source: session_source.clone(),
sandbox_policy: session_configuration.sandbox_policy.get(),
permission_profile: &session_configuration.permission_profile(),
windows_sandbox_level: session_configuration.windows_sandbox_level,
})
.with_unified_exec_shell_mode_for_session(
@@ -453,7 +457,7 @@ impl Session {
&session_source,
sub_id.clone(),
cwd.clone(),
session_configuration.sandbox_policy.get(),
&session_configuration.permission_profile(),
session_configuration.windows_sandbox_level,
));
let (current_date, timezone) = local_time_context();
@@ -481,6 +485,7 @@ impl Session {
collaboration_mode: session_configuration.collaboration_mode.clone(),
personality: session_configuration.personality,
approval_policy: session_configuration.approval_policy.clone(),
permission_profile: session_configuration.permission_profile(),
sandbox_policy: session_configuration.sandbox_policy.clone(),
file_system_sandbox_policy: session_configuration.file_system_sandbox_policy.clone(),
network_sandbox_policy: session_configuration.network_sandbox_policy,
+6 -6
View File
@@ -33,10 +33,9 @@ use codex_shell_command::parse_command::parse_command;
use super::SessionTask;
use super::SessionTaskContext;
use crate::session::session::Session;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
const USER_SHELL_TIMEOUT_MS: u64 = 60 * 60 * 1000; // 1 hour
@@ -157,7 +156,7 @@ pub(crate) async fn execute_user_shell_command(
)
.await;
let sandbox_policy = SandboxPolicy::DangerFullAccess;
let permission_profile = PermissionProfile::Disabled;
let exec_env = ExecRequest {
command: exec_command.clone(),
cwd: cwd.clone(),
@@ -177,9 +176,10 @@ pub(crate) async fn execute_user_shell_command(
.config
.permissions
.windows_sandbox_private_desktop,
sandbox_policy: sandbox_policy.clone(),
file_system_sandbox_policy: FileSystemSandboxPolicy::from(&sandbox_policy),
network_sandbox_policy: NetworkSandboxPolicy::from(&sandbox_policy),
permission_profile: permission_profile.clone(),
sandbox_policy: SandboxPolicy::DangerFullAccess,
file_system_sandbox_policy: permission_profile.file_system_sandbox_policy(),
network_sandbox_policy: permission_profile.network_sandbox_policy(),
windows_sandbox_filesystem_overrides: None,
arg0: None,
};
@@ -269,13 +269,10 @@ pub(crate) fn apply_spawn_agent_runtime_overrides(
config.cwd = turn.cwd.clone();
config
.permissions
.sandbox_policy
.set(turn.sandbox_policy.get().clone())
.set_permission_profile(turn.permission_profile(), turn.cwd.as_path())
.map_err(|err| {
FunctionCallError::RespondToModel(format!("sandbox_policy is invalid: {err}"))
FunctionCallError::RespondToModel(format!("permission_profile is invalid: {err}"))
})?;
config.permissions.file_system_sandbox_policy = turn.file_system_sandbox_policy.clone();
config.permissions.network_sandbox_policy = turn.network_sandbox_policy;
Ok(())
}
@@ -28,8 +28,10 @@ use codex_protocol::ThreadId;
use codex_protocol::models::BaseInstructions;
use codex_protocol::models::ContentItem;
use codex_protocol::models::FunctionCallOutputBody;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::AgentStatus;
use codex_protocol::protocol::AskForApproval;
@@ -1958,6 +1960,11 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
let expected_file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&expected_sandbox, &turn.cwd);
let expected_network_sandbox_policy = NetworkSandboxPolicy::from(&expected_sandbox);
let expected_permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&expected_sandbox),
&expected_file_system_sandbox_policy,
expected_network_sandbox_policy,
);
turn.approval_policy
.set(AskForApproval::OnRequest)
.expect("approval policy should be set");
@@ -1966,6 +1973,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
.expect("sandbox policy should be set");
turn.file_system_sandbox_policy = expected_file_system_sandbox_policy.clone();
turn.network_sandbox_policy = expected_network_sandbox_policy;
turn.permission_profile = expected_permission_profile.clone();
assert_ne!(
expected_sandbox,
turn.config.permissions.sandbox_policy.get().clone(),
@@ -2004,6 +2012,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
.await;
assert_eq!(snapshot.sandbox_policy, expected_sandbox);
assert_eq!(snapshot.approval_policy, AskForApproval::OnRequest);
assert_eq!(snapshot.permission_profile, expected_permission_profile);
let child_thread = manager
.get_thread(agent_id)
.await
@@ -2017,6 +2026,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
child_turn.network_sandbox_policy,
expected_network_sandbox_policy
);
assert_eq!(child_turn.permission_profile(), expected_permission_profile);
}
#[tokio::test]
@@ -3477,11 +3487,17 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, &turn.cwd);
let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy);
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
turn.sandbox_policy
.set(sandbox_policy)
.expect("sandbox policy set");
turn.file_system_sandbox_policy = file_system_sandbox_policy.clone();
turn.file_system_sandbox_policy = file_system_sandbox_policy;
turn.network_sandbox_policy = network_sandbox_policy;
turn.permission_profile = permission_profile.clone();
turn.approval_policy
.set(AskForApproval::OnRequest)
.expect("approval policy set");
@@ -3505,11 +3521,8 @@ async fn build_agent_spawn_config_uses_turn_context_values() {
.expect("approval policy set");
expected
.permissions
.sandbox_policy
.set(turn.sandbox_policy.get().clone())
.expect("sandbox policy set");
expected.permissions.file_system_sandbox_policy = file_system_sandbox_policy;
expected.permissions.network_sandbox_policy = network_sandbox_policy;
.set_permission_profile(permission_profile, turn.cwd.as_path())
.expect("permission profile set");
assert_eq!(config, expected);
}
+1 -3
View File
@@ -1075,9 +1075,7 @@ impl JsReplManager {
let exec_env = sandbox
.transform(SandboxTransformRequest {
command,
policy: &turn.sandbox_policy,
file_system_policy: &turn.file_system_sandbox_policy,
network_policy: turn.network_sandbox_policy,
permissions: &turn.permission_profile,
sandbox: sandbox_type,
enforce_managed_network: managed_network_active,
network: None,
+2 -6
View File
@@ -206,9 +206,7 @@ impl ToolOrchestrator {
let use_legacy_landlock = turn_ctx.features.use_legacy_landlock();
let initial_attempt = SandboxAttempt {
sandbox: initial_sandbox,
policy: &turn_ctx.sandbox_policy,
file_system_policy: &turn_ctx.file_system_sandbox_policy,
network_policy: turn_ctx.network_sandbox_policy,
permissions: &turn_ctx.permission_profile,
enforce_managed_network: managed_network_active,
manager: &self.sandbox,
sandbox_cwd: &turn_ctx.cwd,
@@ -325,9 +323,7 @@ impl ToolOrchestrator {
let escalated_attempt = SandboxAttempt {
sandbox: SandboxType::None,
policy: &turn_ctx.sandbox_policy,
file_system_policy: &turn_ctx.file_system_sandbox_policy,
network_policy: turn_ctx.network_sandbox_policy,
permissions: &turn_ctx.permission_profile,
enforce_managed_network: managed_network_active,
manager: &self.sandbox,
sandbox_cwd: &turn_ctx.cwd,
+18 -17
View File
@@ -8,7 +8,8 @@ use crate::hook_runtime::record_additional_contexts;
use crate::hook_runtime::run_post_tool_use_hooks;
use crate::hook_runtime::run_pre_tool_use_hooks;
use crate::memories::usage::emit_metric_for_tool_read;
use crate::sandbox_tags::sandbox_tag;
use crate::sandbox_tags::permission_profile_policy_tag;
use crate::sandbox_tags::permission_profile_sandbox_tag;
use crate::session::turn_context::TurnContext;
use crate::tools::context::FunctionToolOutput;
use crate::tools::context::ToolInvocation;
@@ -25,7 +26,6 @@ use codex_hooks::HookToolInputLocalShell;
use codex_hooks::HookToolKind;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::SandboxPolicy;
use codex_tools::ConfiguredToolSpec;
use codex_tools::ToolName;
use codex_tools::ToolSpec;
@@ -274,14 +274,17 @@ impl ToolRegistry {
let metric_tags = [
(
"sandbox",
sandbox_tag(
&invocation.turn.sandbox_policy,
permission_profile_sandbox_tag(
&invocation.turn.permission_profile,
invocation.turn.windows_sandbox_level,
),
),
(
"sandbox_policy",
sandbox_policy_tag(&invocation.turn.sandbox_policy),
permission_profile_policy_tag(
&invocation.turn.permission_profile,
invocation.turn.cwd.as_path(),
),
),
];
let (mcp_server, mcp_server_origin) = match &invocation.payload {
@@ -568,15 +571,6 @@ fn unsupported_tool_call_message(payload: &ToolPayload, tool_name: &ToolName) ->
}
}
fn sandbox_policy_tag(policy: &SandboxPolicy) -> &'static str {
match policy {
SandboxPolicy::ReadOnly { .. } => "read-only",
SandboxPolicy::WorkspaceWrite { .. } => "workspace-write",
SandboxPolicy::DangerFullAccess => "danger-full-access",
SandboxPolicy::ExternalSandbox { .. } => "external-sandbox",
}
}
// Hooks use a separate wire-facing input type so hook payload JSON stays stable
// and decoupled from core's internal tool runtime representation.
impl From<&ToolPayload> for HookToolInput {
@@ -661,9 +655,16 @@ async fn dispatch_after_tool_use_hook(
success: dispatch.success,
duration_ms: u64::try_from(dispatch.duration.as_millis()).unwrap_or(u64::MAX),
mutating: dispatch.mutating,
sandbox: sandbox_tag(&turn.sandbox_policy, turn.windows_sandbox_level)
.to_string(),
sandbox_policy: sandbox_policy_tag(&turn.sandbox_policy).to_string(),
sandbox: permission_profile_sandbox_tag(
&turn.permission_profile,
turn.windows_sandbox_level,
)
.to_string(),
sandbox_policy: permission_profile_policy_tag(
&turn.permission_profile,
turn.cwd.as_path(),
)
.to_string(),
output_preview: dispatch.output_preview.clone(),
},
},
@@ -24,8 +24,6 @@ use codex_protocol::error::SandboxErr;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::exec_output::StreamOutput;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
@@ -35,8 +33,7 @@ use codex_protocol::protocol::FileChange;
use codex_protocol::protocol::ReviewDecision;
use codex_sandboxing::SandboxType;
use codex_sandboxing::SandboxablePreference;
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
use codex_sandboxing::policy_transforms::effective_permission_profile;
use codex_utils_absolute_path::AbsolutePathBuf;
use futures::future::BoxFuture;
use std::path::PathBuf;
@@ -80,19 +77,8 @@ impl ApplyPatchRuntime {
return None;
}
let file_system_policy = effective_file_system_sandbox_policy(
attempt.file_system_policy,
req.additional_permissions.as_ref(),
);
let network_policy = effective_network_sandbox_policy(
attempt.network_policy,
req.additional_permissions.as_ref(),
);
let permissions = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(attempt.policy),
&file_system_policy,
network_policy,
);
let permissions =
effective_permission_profile(attempt.permissions, req.additional_permissions.as_ref());
Some(FileSystemSandboxContext {
permissions,
cwd: Some(attempt.sandbox_cwd.clone()),
@@ -138,12 +138,14 @@ fn file_system_sandbox_context_uses_active_attempt() {
};
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let file_system_policy = FileSystemSandboxPolicy::from(&sandbox_policy);
let permissions = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
let manager = SandboxManager::new();
let attempt = SandboxAttempt {
sandbox: SandboxType::MacosSeatbelt,
policy: &sandbox_policy,
file_system_policy: &file_system_policy,
network_policy: NetworkSandboxPolicy::Restricted,
permissions: &permissions,
enforce_managed_network: false,
manager: &manager,
sandbox_cwd: &path,
@@ -190,14 +192,11 @@ fn no_sandbox_attempt_has_no_file_system_context() {
additional_permissions: None,
permissions_preapproved: false,
};
let sandbox_policy = SandboxPolicy::DangerFullAccess;
let file_system_policy = FileSystemSandboxPolicy::from(&sandbox_policy);
let permissions = PermissionProfile::Disabled;
let manager = SandboxManager::new();
let attempt = SandboxAttempt {
sandbox: SandboxType::None,
policy: &sandbox_policy,
file_system_policy: &file_system_policy,
network_policy: NetworkSandboxPolicy::Enabled,
permissions: &permissions,
enforce_managed_network: false,
manager: &manager,
sandbox_cwd: &path,
@@ -139,6 +139,7 @@ pub(super) async fn try_run_zsh_fork(
windows_sandbox_policy_cwd: sandbox_policy_cwd,
windows_sandbox_level,
windows_sandbox_private_desktop: _windows_sandbox_private_desktop,
permission_profile,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
@@ -156,6 +157,7 @@ pub(super) async fn try_run_zsh_fork(
let command_executor = CoreShellCommandExecutor {
command,
cwd: sandbox_cwd,
permission_profile,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
@@ -254,6 +256,7 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
let command_executor = CoreShellCommandExecutor {
command: exec_request.command.clone(),
cwd: exec_request.cwd.clone(),
permission_profile: exec_request.permission_profile.clone(),
sandbox_policy: exec_request.sandbox_policy.clone(),
file_system_sandbox_policy: exec_request.file_system_sandbox_policy.clone(),
network_sandbox_policy: exec_request.network_sandbox_policy,
@@ -743,6 +746,7 @@ fn commands_for_intercepted_exec_policy(
struct CoreShellCommandExecutor {
command: Vec<String>,
cwd: AbsolutePathBuf,
permission_profile: PermissionProfile,
sandbox_policy: SandboxPolicy,
file_system_sandbox_policy: FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
@@ -760,9 +764,7 @@ struct PrepareSandboxedExecParams<'a> {
command: Vec<String>,
workdir: &'a AbsolutePathBuf,
env: HashMap<String, String>,
sandbox_policy: &'a SandboxPolicy,
file_system_sandbox_policy: &'a FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: &'a PermissionProfile,
additional_permissions: Option<AdditionalPermissionProfile>,
}
@@ -798,6 +800,7 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
windows_sandbox_policy_cwd: self.sandbox_policy_cwd.clone(),
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: false,
permission_profile: self.permission_profile.clone(),
sandbox_policy: self.sandbox_policy.clone(),
file_system_sandbox_policy: self.file_system_sandbox_policy.clone(),
network_sandbox_policy: self.network_sandbox_policy,
@@ -846,9 +849,7 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
command,
workdir,
env,
sandbox_policy: &self.sandbox_policy,
file_system_sandbox_policy: &self.file_system_sandbox_policy,
network_sandbox_policy: self.network_sandbox_policy,
permission_profile: &self.permission_profile,
additional_permissions: None,
})?
}
@@ -860,9 +861,7 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
command,
workdir,
env,
sandbox_policy: &self.sandbox_policy,
file_system_sandbox_policy: &self.file_system_sandbox_policy,
network_sandbox_policy: self.network_sandbox_policy,
permission_profile: &self.permission_profile,
additional_permissions: Some(permission_profile),
})?
}
@@ -870,15 +869,11 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
permissions,
)) => {
// Use a fully specified permission profile instead of merging into the turn policy.
let (file_system_sandbox_policy, network_sandbox_policy) =
permissions.permission_profile.to_runtime_permissions();
self.prepare_sandboxed_exec(PrepareSandboxedExecParams {
command,
workdir,
env,
sandbox_policy: &permissions.sandbox_policy,
file_system_sandbox_policy: &file_system_sandbox_policy,
network_sandbox_policy,
permission_profile: &permissions.permission_profile,
additional_permissions: None,
})?
}
@@ -898,17 +893,17 @@ impl CoreShellCommandExecutor {
command,
workdir,
env,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
permission_profile,
additional_permissions,
} = params;
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let (program, args) = command
.split_first()
.ok_or_else(|| anyhow::anyhow!("prepared command must not be empty"))?;
let sandbox_manager = SandboxManager::new();
let sandbox = sandbox_manager.select_initial(
file_system_sandbox_policy,
&file_system_sandbox_policy,
network_sandbox_policy,
SandboxablePreference::Auto,
self.windows_sandbox_level,
@@ -927,9 +922,7 @@ impl CoreShellCommandExecutor {
};
let exec_request = sandbox_manager.transform(SandboxTransformRequest {
command,
policy: sandbox_policy,
file_system_policy: file_system_sandbox_policy,
network_policy: network_sandbox_policy,
permissions: permission_profile,
sandbox,
enforce_managed_network: self.network.is_some(),
network: self.network.as_ref(),
+2 -7
View File
@@ -17,7 +17,6 @@ use codex_protocol::approvals::NetworkApprovalContext;
use codex_protocol::error::CodexErr;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::ReviewDecision;
#[cfg(test)]
@@ -357,9 +356,7 @@ pub(crate) trait ToolRuntime<Req, Out>: Approvable<Req> + Sandboxable {
pub(crate) struct SandboxAttempt<'a> {
pub sandbox: SandboxType,
pub policy: &'a codex_protocol::protocol::SandboxPolicy,
pub file_system_policy: &'a FileSystemSandboxPolicy,
pub network_policy: NetworkSandboxPolicy,
pub permissions: &'a codex_protocol::models::PermissionProfile,
pub enforce_managed_network: bool,
pub(crate) manager: &'a SandboxManager,
pub(crate) sandbox_cwd: &'a AbsolutePathBuf,
@@ -379,9 +376,7 @@ impl<'a> SandboxAttempt<'a> {
self.manager
.transform(SandboxTransformRequest {
command,
policy: self.policy,
file_system_policy: self.file_system_policy,
network_policy: self.network_policy,
permissions: self.permissions,
sandbox: self.sandbox,
enforce_managed_network: self.enforce_managed_network,
network,
+20 -17
View File
@@ -12,6 +12,7 @@ use codex_models_manager::bundled_models_response;
use codex_models_manager::model_info::with_config_overrides;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::protocol::SandboxPolicy;
@@ -230,7 +231,7 @@ async fn multi_agent_v2_tools_config() -> ToolsConfig {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
})
}
@@ -308,7 +309,9 @@ async fn model_provided_unified_exec_is_blocked_for_windows_sandboxed_policies()
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::new_workspace_write_policy(),
permission_profile: &PermissionProfile::from_legacy_sandbox_policy(
&SandboxPolicy::new_workspace_write_policy(),
),
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
});
@@ -334,7 +337,7 @@ async fn get_memory_requires_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -366,7 +369,7 @@ async fn assert_model_tools(
image_generation_tool_auth_allowed: true,
web_search_mode,
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let router = ToolRouter::from_config(
@@ -649,7 +652,7 @@ async fn test_build_specs_default_shell_present() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -684,7 +687,7 @@ async fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let user_shell = Shell {
@@ -806,7 +809,7 @@ async fn tool_suggest_requires_apps_and_plugins_features() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs_with_discoverable_tools(
@@ -842,7 +845,7 @@ async fn search_tool_description_handles_no_enabled_mcp_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -876,7 +879,7 @@ async fn search_tool_description_falls_back_to_connector_name_without_descriptio
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -927,7 +930,7 @@ async fn search_tool_registers_namespaced_mcp_tool_aliases() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1012,7 +1015,7 @@ async fn direct_mcp_tools_register_namespaced_handlers() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1049,7 +1052,7 @@ async fn unavailable_mcp_tools_are_exposed_as_dummy_function_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1098,7 +1101,7 @@ async fn test_mcp_tool_property_missing_type_defaults_to_string() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1161,7 +1164,7 @@ async fn test_mcp_tool_preserves_integer_schema() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1223,7 +1226,7 @@ async fn test_mcp_tool_array_without_items_gets_default_string_items() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1287,7 +1290,7 @@ async fn test_mcp_tool_anyof_defaults_to_string() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1356,7 +1359,7 @@ async fn test_get_openai_tools_mcp_tools_with_additional_properties_schema() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
+6 -4
View File
@@ -8,13 +8,13 @@ use serde::Serialize;
use serde_json::Value;
use tokio::task::JoinHandle;
use crate::sandbox_tags::sandbox_tag;
use crate::sandbox_tags::permission_profile_sandbox_tag;
use codex_git_utils::get_git_remote_urls_assume_git_repo;
use codex_git_utils::get_git_repo_root;
use codex_git_utils::get_has_changes;
use codex_git_utils::get_head_commit_hash;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::SessionSource;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -163,11 +163,13 @@ impl TurnMetadataState {
session_source: &SessionSource,
turn_id: String,
cwd: AbsolutePathBuf,
sandbox_policy: &SandboxPolicy,
permission_profile: &PermissionProfile,
windows_sandbox_level: WindowsSandboxLevel,
) -> Self {
let repo_root = get_git_repo_root(&cwd).map(|root| root.to_string_lossy().into_owned());
let sandbox = Some(sandbox_tag(sandbox_policy, windows_sandbox_level).to_string());
let sandbox = Some(
permission_profile_sandbox_tag(permission_profile, windows_sandbox_level).to_string(),
);
let base_metadata = build_turn_metadata_bag(
Some(session_id),
session_source.thread_source_name(),
+11 -5
View File
@@ -1,5 +1,8 @@
use super::*;
use crate::sandbox_tags::sandbox_tag;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use core_test_support::PathBufExt;
@@ -70,13 +73,14 @@ fn turn_metadata_state_uses_platform_sandbox_tag() {
let temp_dir = TempDir::new().expect("temp dir");
let cwd = temp_dir.path().abs();
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let permission_profile = PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy);
let state = TurnMetadataState::new(
"session-a".to_string(),
&SessionSource::Exec,
"turn-a".to_string(),
cwd,
&sandbox_policy,
&permission_profile,
WindowsSandboxLevel::Disabled,
);
@@ -97,7 +101,8 @@ fn turn_metadata_state_uses_platform_sandbox_tag() {
fn turn_metadata_state_classifies_subagent_thread_source() {
let temp_dir = TempDir::new().expect("temp dir");
let cwd = temp_dir.path().abs();
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy(&SandboxPolicy::new_read_only_policy());
let session_source = SessionSource::SubAgent(SubAgentSource::Review);
let state = TurnMetadataState::new(
@@ -105,7 +110,7 @@ fn turn_metadata_state_classifies_subagent_thread_source() {
&session_source,
"turn-a".to_string(),
cwd,
&sandbox_policy,
&permission_profile,
WindowsSandboxLevel::Disabled,
);
@@ -120,14 +125,15 @@ fn turn_metadata_state_classifies_subagent_thread_source() {
fn turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields() {
let temp_dir = TempDir::new().expect("temp dir");
let cwd = temp_dir.path().abs();
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy(&SandboxPolicy::new_read_only_policy());
let state = TurnMetadataState::new(
"session-a".to_string(),
&SessionSource::Exec,
"turn-a".to_string(),
cwd,
&sandbox_policy,
&permission_profile,
WindowsSandboxLevel::Disabled,
);
state.set_responsesapi_client_metadata(HashMap::from([
+2 -6
View File
@@ -55,9 +55,7 @@ fn test_exec_request(
env: HashMap<String, String>,
) -> ExecRequest {
let windows_sandbox_private_desktop = false;
let sandbox_policy = turn.sandbox_policy.get().clone();
let file_system_sandbox_policy = turn.file_system_sandbox_policy.clone();
let network_sandbox_policy = turn.network_sandbox_policy;
let permission_profile = turn.permission_profile();
let network = None;
let arg0 = None;
ExecRequest::new(
@@ -70,9 +68,7 @@ fn test_exec_request(
SandboxType::None,
turn.windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
permission_profile,
arg0,
)
}
@@ -71,6 +71,16 @@ fn exec_server_params_use_env_policy_overlay_contract() {
.expect("current dir")
.try_into()
.expect("absolute path");
let sandbox_policy = codex_protocol::protocol::SandboxPolicy::DangerFullAccess;
let file_system_sandbox_policy =
codex_protocol::permissions::FileSystemSandboxPolicy::from(&sandbox_policy);
let network_sandbox_policy = codex_protocol::permissions::NetworkSandboxPolicy::Restricted;
let permission_profile =
codex_protocol::models::PermissionProfile::from_runtime_permissions_with_enforcement(
codex_protocol::models::SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
let request = ExecRequest {
command: vec!["bash".to_string(), "-lc".to_string(), "true".to_string()],
cwd: cwd.clone(),
@@ -99,11 +109,10 @@ fn exec_server_params_use_env_policy_overlay_contract() {
windows_sandbox_policy_cwd: cwd,
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess,
file_system_sandbox_policy: codex_protocol::permissions::FileSystemSandboxPolicy::from(
&codex_protocol::protocol::SandboxPolicy::DangerFullAccess,
),
network_sandbox_policy: codex_protocol::permissions::NetworkSandboxPolicy::Restricted,
permission_profile,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
windows_sandbox_filesystem_overrides: None,
arg0: None,
};
+3 -5
View File
@@ -8,8 +8,7 @@ use codex_core::spawn::CODEX_SANDBOX_ENV_VAR;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::error::Result;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxType;
use codex_sandboxing::get_platform_sandbox;
@@ -52,12 +51,11 @@ where
};
let policy = SandboxPolicy::new_read_only_policy();
let permission_profile = PermissionProfile::from_legacy_sandbox_policy(&policy);
process_exec_tool_call(
params,
&policy,
&FileSystemSandboxPolicy::from(&policy),
NetworkSandboxPolicy::from(&policy),
&permission_profile,
&cwd,
&None,
/*use_legacy_landlock*/ false,
@@ -551,7 +551,10 @@ async fn permissions_message_includes_writable_roots() -> Result<()> {
let mut builder = test_codex().with_config(move |config| {
config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
config.permissions.sandbox_policy = Constrained::allow_any(sandbox_policy_for_config);
config
.permissions
.set_legacy_sandbox_policy(sandbox_policy_for_config, config.cwd.as_path())
.expect("test sandbox policy should be allowed");
config.config_layer_stack = ConfigLayerStack::default();
});
let test = builder.build(&server).await?;
+12 -65
View File
@@ -1,14 +1,13 @@
use std::collections::HashMap;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::FileSystemSpecialPath;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::ReadOnlyAccess;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxExecRequest;
use codex_sandboxing::SandboxManager;
@@ -61,31 +60,27 @@ impl FileSystemSandboxRunner {
add_helper_runtime_permissions(&mut file_system_policy, &helper_read_roots, cwd.as_path());
normalize_file_system_policy_root_aliases(&mut file_system_policy);
let network_policy = NetworkSandboxPolicy::Restricted;
let sandbox_policy =
compatibility_sandbox_policy(&file_system_policy, network_policy, cwd.as_path());
let command = self.sandbox_exec_request(
&sandbox_policy,
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
sandbox.permissions.enforcement(),
&file_system_policy,
network_policy,
&cwd,
sandbox,
)?;
);
let command = self.sandbox_exec_request(&permission_profile, &cwd, sandbox)?;
let request_json = serde_json::to_vec(&request).map_err(json_error)?;
run_command(command, request_json).await
}
fn sandbox_exec_request(
&self,
sandbox_policy: &SandboxPolicy,
file_system_policy: &FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
permission_profile: &PermissionProfile,
cwd: &AbsolutePathBuf,
sandbox_context: &FileSystemSandboxContext,
) -> Result<SandboxExecRequest, JSONRPCErrorError> {
let helper = &self.runtime_paths.codex_self_exe;
let sandbox_manager = SandboxManager::new();
let (file_system_policy, network_policy) = permission_profile.to_runtime_permissions();
let sandbox = sandbox_manager.select_initial(
file_system_policy,
&file_system_policy,
network_policy,
SandboxablePreference::Auto,
sandbox_context.windows_sandbox_level,
@@ -101,9 +96,7 @@ impl FileSystemSandboxRunner {
sandbox_manager
.transform(SandboxTransformRequest {
command,
policy: sandbox_policy,
file_system_policy,
network_policy,
permissions: permission_profile,
sandbox,
enforce_managed_network: false,
network: None,
@@ -180,45 +173,6 @@ fn add_helper_runtime_permissions(
}
}
fn compatibility_sandbox_policy(
file_system_policy: &FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
cwd: &std::path::Path,
) -> SandboxPolicy {
file_system_policy
.to_legacy_sandbox_policy(network_policy, cwd)
.unwrap_or_else(|_| compatibility_workspace_write_policy(file_system_policy, cwd))
}
fn compatibility_workspace_write_policy(
file_system_policy: &FileSystemSandboxPolicy,
cwd: &std::path::Path,
) -> SandboxPolicy {
let read_only_access = if file_system_policy.has_full_disk_read_access() {
ReadOnlyAccess::FullAccess
} else {
ReadOnlyAccess::Restricted {
include_platform_defaults: file_system_policy.include_platform_defaults(),
readable_roots: file_system_policy.get_readable_roots_with_cwd(cwd),
}
};
let cwd_abs = AbsolutePathBuf::from_absolute_path(cwd).ok();
let writable_roots = file_system_policy
.get_writable_roots_with_cwd(cwd)
.into_iter()
.map(|root| root.root)
.filter(|root| cwd_abs.as_ref() != Some(root))
.collect();
SandboxPolicy::WorkspaceWrite {
writable_roots,
read_only_access,
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
}
}
fn normalize_file_system_policy_root_aliases(file_system_policy: &mut FileSystemSandboxPolicy) {
for entry in &mut file_system_policy.entries {
if let FileSystemPath::Path { path } = &mut entry.path {
@@ -357,7 +311,6 @@ mod tests {
use super::FileSystemSandboxRunner;
use super::add_helper_runtime_permissions;
use super::compatibility_sandbox_policy;
use super::helper_env;
use super::helper_env_from_vars;
use super::helper_env_key_is_allowed;
@@ -498,18 +451,12 @@ mod tests {
let file_system_policy =
restricted_policy(vec![path_entry(cwd.clone(), FileSystemAccessMode::Write)]);
let network_policy = NetworkSandboxPolicy::Restricted;
let sandbox_policy =
compatibility_sandbox_policy(&file_system_policy, network_policy, cwd.as_path());
let permission_profile =
PermissionProfile::from_runtime_permissions(&file_system_policy, network_policy);
let sandbox_context = sandbox_context_with_cwd(&file_system_policy, cwd.clone());
let request = runner
.sandbox_exec_request(
&sandbox_policy,
&file_system_policy,
network_policy,
&cwd,
&sandbox_context,
)
.sandbox_exec_request(&permission_profile, &cwd, &sandbox_context)
.expect("sandbox exec request");
assert_eq!(request.env.get(&path_key), Some(&path));
+2 -5
View File
@@ -24,8 +24,7 @@ async fn spawn_command_under_sandbox(
use codex_core::exec::build_exec_request;
use codex_core::sandboxing::SandboxPermissions;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::models::PermissionProfile;
use std::process::Stdio;
let codex_linux_sandbox_exe = None;
@@ -43,9 +42,7 @@ async fn spawn_command_under_sandbox(
justification: None,
arg0: None,
},
sandbox_policy,
&FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(sandbox_policy, sandbox_cwd),
NetworkSandboxPolicy::from(sandbox_policy),
&PermissionProfile::from_legacy_sandbox_policy(sandbox_policy),
sandbox_cwd,
&codex_linux_sandbox_exe,
/*use_legacy_landlock*/ false,
+10 -6
View File
@@ -10,6 +10,8 @@ use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::error::CodexErr;
use codex_protocol::error::Result;
use codex_protocol::error::SandboxErr;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -134,12 +136,15 @@ async fn run_cmd_result_with_policies(
};
let sandbox_program = env!("CARGO_BIN_EXE_codex-linux-sandbox");
let codex_linux_sandbox_exe = Some(PathBuf::from(sandbox_program));
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
);
process_exec_tool_call(
params,
&sandbox_policy,
&file_system_sandbox_policy,
network_sandbox_policy,
&permission_profile,
&sandbox_cwd,
&codex_linux_sandbox_exe,
use_legacy_landlock,
@@ -396,11 +401,10 @@ async fn assert_network_blocked(cmd: &[&str]) {
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let sandbox_program = env!("CARGO_BIN_EXE_codex-linux-sandbox");
let codex_linux_sandbox_exe: Option<PathBuf> = Some(PathBuf::from(sandbox_program));
let permission_profile = PermissionProfile::from_legacy_sandbox_policy(&sandbox_policy);
let result = process_exec_tool_call(
params,
&sandbox_policy,
&FileSystemSandboxPolicy::from(&sandbox_policy),
NetworkSandboxPolicy::from(&sandbox_policy),
&permission_profile,
&sandbox_cwd,
&codex_linux_sandbox_exe,
/*use_legacy_landlock*/ false,
+1
View File
@@ -17,6 +17,7 @@ pub use manager::SandboxTransformError;
pub use manager::SandboxTransformRequest;
pub use manager::SandboxType;
pub use manager::SandboxablePreference;
pub use manager::compatibility_sandbox_policy_for_permission_profile;
pub use manager::get_platform_sandbox;
use codex_protocol::error::CodexErr;
+69 -17
View File
@@ -5,15 +5,15 @@ use crate::bwrap::is_wsl1;
use crate::landlock::CODEX_LINUX_SANDBOX_ARG0;
use crate::landlock::allow_network_for_proxy;
use crate::landlock::create_linux_sandbox_command_args_for_policies;
use crate::policy_transforms::EffectiveSandboxPermissions;
use crate::policy_transforms::effective_file_system_sandbox_policy;
use crate::policy_transforms::effective_network_sandbox_policy;
use crate::policy_transforms::effective_permission_profile;
use crate::policy_transforms::should_require_platform_sandbox;
use codex_network_proxy::NetworkProxy;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::ReadOnlyAccess;
use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap;
@@ -80,6 +80,7 @@ pub struct SandboxExecRequest {
pub sandbox: SandboxType,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
pub permission_profile: PermissionProfile,
pub sandbox_policy: SandboxPolicy,
pub file_system_sandbox_policy: FileSystemSandboxPolicy,
pub network_sandbox_policy: NetworkSandboxPolicy,
@@ -91,9 +92,7 @@ pub struct SandboxExecRequest {
/// This keeps call sites self-documenting when several fields are optional.
pub struct SandboxTransformRequest<'a> {
pub command: SandboxCommand,
pub policy: &'a SandboxPolicy,
pub file_system_policy: &'a FileSystemSandboxPolicy,
pub network_policy: NetworkSandboxPolicy,
pub permissions: &'a PermissionProfile,
pub sandbox: SandboxType,
pub enforce_managed_network: bool,
// TODO(viyatb): Evaluate switching this to Option<Arc<NetworkProxy>>
@@ -174,9 +173,7 @@ impl SandboxManager {
) -> Result<SandboxExecRequest, SandboxTransformError> {
let SandboxTransformRequest {
mut command,
policy,
file_system_policy,
network_policy,
permissions,
sandbox,
enforce_managed_network,
network,
@@ -187,15 +184,16 @@ impl SandboxManager {
windows_sandbox_private_desktop,
} = request;
let additional_permissions = command.additional_permissions.take();
let EffectiveSandboxPermissions {
sandbox_policy: effective_policy,
} = EffectiveSandboxPermissions::new(policy, additional_permissions.as_ref());
let effective_file_system_policy = effective_file_system_sandbox_policy(
file_system_policy,
additional_permissions.as_ref(),
let effective_permission_profile =
effective_permission_profile(permissions, additional_permissions.as_ref());
let (effective_file_system_policy, effective_network_policy) =
effective_permission_profile.to_runtime_permissions();
let effective_policy = compatibility_sandbox_policy_for_permission_profile(
&effective_permission_profile,
&effective_file_system_policy,
effective_network_policy,
sandbox_policy_cwd,
);
let effective_network_policy =
effective_network_sandbox_policy(network_policy, additional_permissions.as_ref());
let mut argv = Vec::with_capacity(1 + command.args.len());
argv.push(command.program);
argv.extend(command.args.into_iter().map(OsString::from));
@@ -264,6 +262,7 @@ impl SandboxManager {
sandbox,
windows_sandbox_level,
windows_sandbox_private_desktop,
permission_profile: effective_permission_profile,
sandbox_policy: effective_policy,
file_system_sandbox_policy: effective_file_system_policy,
network_sandbox_policy: effective_network_policy,
@@ -272,6 +271,59 @@ impl SandboxManager {
}
}
pub fn compatibility_sandbox_policy_for_permission_profile(
permissions: &PermissionProfile,
file_system_policy: &FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
cwd: &Path,
) -> SandboxPolicy {
permissions
.to_legacy_sandbox_policy(cwd)
.unwrap_or_else(|_| {
compatibility_workspace_write_policy(file_system_policy, network_policy, cwd)
})
}
fn compatibility_workspace_write_policy(
file_system_policy: &FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
cwd: &Path,
) -> SandboxPolicy {
let read_only_access = if file_system_policy.has_full_disk_read_access() {
ReadOnlyAccess::FullAccess
} else {
ReadOnlyAccess::Restricted {
include_platform_defaults: file_system_policy.include_platform_defaults(),
readable_roots: file_system_policy.get_readable_roots_with_cwd(cwd),
}
};
let cwd_abs = AbsolutePathBuf::from_absolute_path(cwd).ok();
let writable_roots = file_system_policy
.get_writable_roots_with_cwd(cwd)
.into_iter()
.map(|root| root.root)
.filter(|root| cwd_abs.as_ref() != Some(root))
.collect();
let tmpdir_writable = std::env::var_os("TMPDIR")
.filter(|tmpdir| !tmpdir.is_empty())
.and_then(|tmpdir| {
AbsolutePathBuf::from_absolute_path(std::path::PathBuf::from(tmpdir)).ok()
})
.is_some_and(|tmpdir| file_system_policy.can_write_path_with_cwd(tmpdir.as_path(), cwd));
let slash_tmp = Path::new("/tmp");
let slash_tmp_writable = slash_tmp.is_absolute()
&& slash_tmp.is_dir()
&& file_system_policy.can_write_path_with_cwd(slash_tmp, cwd);
SandboxPolicy::WorkspaceWrite {
writable_roots,
read_only_access,
network_access: network_policy.is_enabled(),
exclude_tmpdir_env_var: !tmpdir_writable,
exclude_slash_tmp: !slash_tmp_writable,
}
}
#[cfg(target_os = "linux")]
fn ensure_linux_bubblewrap_is_supported(
file_system_sandbox_policy: &FileSystemSandboxPolicy,
+34 -36
View File
@@ -5,9 +5,10 @@ use super::SandboxType;
use super::SandboxablePreference;
use super::get_platform_sandbox;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::AdditionalPermissionProfile as PermissionProfile;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -15,7 +16,6 @@ use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::FileSystemSpecialPath;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::NetworkAccess;
use codex_protocol::protocol::ReadOnlyAccess;
use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use dunce::canonicalize;
@@ -75,6 +75,10 @@ fn restricted_file_system_uses_platform_sandbox_without_managed_network() {
fn transform_preserves_unrestricted_file_system_policy_for_restricted_network() {
let manager = SandboxManager::new();
let cwd = AbsolutePathBuf::current_dir().expect("current dir");
let permissions = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::unrestricted(),
NetworkSandboxPolicy::Restricted,
);
let exec_request = manager
.transform(SandboxTransformRequest {
command: SandboxCommand {
@@ -84,11 +88,7 @@ fn transform_preserves_unrestricted_file_system_policy_for_restricted_network()
env: HashMap::new(),
additional_permissions: None,
},
policy: &SandboxPolicy::ExternalSandbox {
network_access: NetworkAccess::Restricted,
},
file_system_policy: &FileSystemSandboxPolicy::unrestricted(),
network_policy: NetworkSandboxPolicy::Restricted,
permissions: &permissions,
sandbox: SandboxType::None,
enforce_managed_network: false,
network: None,
@@ -114,6 +114,9 @@ fn transform_preserves_unrestricted_file_system_policy_for_restricted_network()
fn transform_additional_permissions_enable_network_for_external_sandbox() {
let manager = SandboxManager::new();
let cwd = AbsolutePathBuf::current_dir().expect("current dir");
let permissions = PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
};
let temp_dir = TempDir::new().expect("create temp dir");
let path = AbsolutePathBuf::from_absolute_path(
canonicalize(temp_dir.path()).expect("canonicalize temp dir"),
@@ -126,7 +129,7 @@ fn transform_additional_permissions_enable_network_for_external_sandbox() {
args: Vec::new(),
cwd: cwd.clone(),
env: HashMap::new(),
additional_permissions: Some(PermissionProfile {
additional_permissions: Some(AdditionalPermissionProfile {
network: Some(NetworkPermissions {
enabled: Some(true),
}),
@@ -136,11 +139,7 @@ fn transform_additional_permissions_enable_network_for_external_sandbox() {
)),
}),
},
policy: &SandboxPolicy::ExternalSandbox {
network_access: NetworkAccess::Restricted,
},
file_system_policy: &FileSystemSandboxPolicy::unrestricted(),
network_policy: NetworkSandboxPolicy::Restricted,
permissions: &permissions,
sandbox: SandboxType::None,
enforce_managed_network: false,
network: None,
@@ -175,6 +174,24 @@ fn transform_additional_permissions_preserves_denied_entries() {
.expect("absolute temp dir");
let allowed_path = workspace_root.join("allowed");
let denied_path = workspace_root.join("denied");
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_path.clone(),
},
access: FileSystemAccessMode::None,
},
]);
let permissions = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
let exec_request = manager
.transform(SandboxTransformRequest {
command: SandboxCommand {
@@ -182,7 +199,7 @@ fn transform_additional_permissions_preserves_denied_entries() {
args: Vec::new(),
cwd: cwd.clone(),
env: HashMap::new(),
additional_permissions: Some(PermissionProfile {
additional_permissions: Some(AdditionalPermissionProfile {
file_system: Some(FileSystemPermissions::from_read_write_roots(
/*read*/ None,
Some(vec![allowed_path.clone()]),
@@ -190,25 +207,7 @@ fn transform_additional_permissions_preserves_denied_entries() {
..Default::default()
}),
},
policy: &SandboxPolicy::ReadOnly {
access: ReadOnlyAccess::FullAccess,
network_access: false,
},
file_system_policy: &FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_path.clone(),
},
access: FileSystemAccessMode::None,
},
]),
network_policy: NetworkSandboxPolicy::Restricted,
permissions: &permissions,
sandbox: SandboxType::None,
enforce_managed_network: false,
network: None,
@@ -251,6 +250,7 @@ fn transform_linux_seccomp_request(
) -> super::SandboxExecRequest {
let manager = SandboxManager::new();
let cwd = AbsolutePathBuf::current_dir().expect("current dir");
let permissions = PermissionProfile::Disabled;
manager
.transform(SandboxTransformRequest {
command: SandboxCommand {
@@ -260,9 +260,7 @@ fn transform_linux_seccomp_request(
env: HashMap::new(),
additional_permissions: None,
},
policy: &SandboxPolicy::DangerFullAccess,
file_system_policy: &FileSystemSandboxPolicy::unrestricted(),
network_policy: NetworkSandboxPolicy::Enabled,
permissions: &permissions,
sandbox: SandboxType::LinuxSeccomp,
enforce_managed_network: false,
network: None,
@@ -1,6 +1,7 @@
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
@@ -582,6 +583,22 @@ pub fn effective_network_sandbox_policy(
}
}
pub fn effective_permission_profile(
permission_profile: &PermissionProfile,
additional_permissions: Option<&AdditionalPermissionProfile>,
) -> PermissionProfile {
let (file_system_policy, network_policy) = permission_profile.to_runtime_permissions();
let effective_file_system_policy =
effective_file_system_sandbox_policy(&file_system_policy, additional_permissions);
let effective_network_policy =
effective_network_sandbox_policy(network_policy, additional_permissions);
PermissionProfile::from_runtime_permissions_with_enforcement(
permission_profile.enforcement(),
&effective_file_system_policy,
effective_network_policy,
)
}
fn sandbox_policy_with_additional_permissions(
sandbox_policy: &SandboxPolicy,
additional_permissions: &AdditionalPermissionProfile,
+13 -9
View File
@@ -4,13 +4,13 @@ use codex_features::Features;
use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ApplyPatchToolType;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_protocol::openai_models::InputModality;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::openai_models::WebSearchToolType;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -121,7 +121,7 @@ pub struct ToolsConfigParams<'a> {
pub image_generation_tool_auth_allowed: bool,
pub web_search_mode: Option<WebSearchMode>,
pub session_source: SessionSource,
pub sandbox_policy: &'a SandboxPolicy,
pub permission_profile: &'a PermissionProfile,
pub windows_sandbox_level: WindowsSandboxLevel,
}
@@ -134,7 +134,7 @@ impl ToolsConfig {
image_generation_tool_auth_allowed,
web_search_mode,
session_source,
sandbox_policy,
permission_profile,
windows_sandbox_level,
} = params;
let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform);
@@ -169,7 +169,7 @@ impl ToolsConfig {
};
let unified_exec_allowed = unified_exec_allowed_in_environment(
cfg!(target_os = "windows"),
sandbox_policy,
permission_profile,
*windows_sandbox_level,
);
let shell_type = if !features.enabled(Feature::ShellTool) {
@@ -311,15 +311,19 @@ fn supports_image_generation(model_info: &ModelInfo) -> bool {
fn unified_exec_allowed_in_environment(
is_windows: bool,
sandbox_policy: &SandboxPolicy,
permission_profile: &PermissionProfile,
windows_sandbox_level: WindowsSandboxLevel,
) -> bool {
let managed_sandbox_required = match permission_profile {
PermissionProfile::Managed {
file_system,
network,
} => !file_system.to_sandbox_policy().has_full_disk_write_access() || !network.is_enabled(),
PermissionProfile::Disabled | PermissionProfile::External { .. } => false,
};
!(is_windows
&& windows_sandbox_level != WindowsSandboxLevel::Disabled
&& !matches!(
sandbox_policy,
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
))
&& managed_sandbox_required)
}
#[cfg(test)]
+33 -11
View File
@@ -3,9 +3,12 @@ use codex_features::Feature;
use codex_features::Features;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::ManagedFileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::openai_models::ConfigShellToolType;
use codex_protocol::openai_models::InputModality;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
@@ -50,29 +53,48 @@ fn model_info() -> ModelInfo {
}
#[test]
fn unified_exec_is_blocked_for_windows_sandboxed_policies_only() {
fn unified_exec_is_blocked_for_windows_managed_profiles_only() {
assert!(!unified_exec_allowed_in_environment(
/*is_windows*/ true,
&SandboxPolicy::new_read_only_policy(),
&profile_from_legacy(&SandboxPolicy::new_read_only_policy()),
WindowsSandboxLevel::RestrictedToken,
));
assert!(!unified_exec_allowed_in_environment(
/*is_windows*/ true,
&SandboxPolicy::new_workspace_write_policy(),
&profile_from_legacy(&SandboxPolicy::new_workspace_write_policy()),
WindowsSandboxLevel::RestrictedToken,
));
assert!(unified_exec_allowed_in_environment(
/*is_windows*/ true,
&SandboxPolicy::DangerFullAccess,
&PermissionProfile::Disabled,
WindowsSandboxLevel::RestrictedToken,
));
assert!(unified_exec_allowed_in_environment(
/*is_windows*/ true,
&SandboxPolicy::DangerFullAccess,
&PermissionProfile::External {
network: Default::default(),
},
WindowsSandboxLevel::RestrictedToken,
));
assert!(unified_exec_allowed_in_environment(
/*is_windows*/ true,
&PermissionProfile::Managed {
file_system: ManagedFileSystemPermissions::Unrestricted,
network: NetworkSandboxPolicy::Enabled,
},
WindowsSandboxLevel::RestrictedToken,
));
assert!(unified_exec_allowed_in_environment(
/*is_windows*/ true,
&PermissionProfile::Disabled,
WindowsSandboxLevel::Disabled,
));
}
fn profile_from_legacy(sandbox_policy: &SandboxPolicy) -> PermissionProfile {
PermissionProfile::from_legacy_sandbox_policy(sandbox_policy)
}
#[test]
fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
let model_info = model_info();
@@ -88,7 +110,7 @@ fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -148,7 +170,7 @@ fn subagents_keep_request_user_input_mode_config_and_agent_jobs_workers_opt_in_b
session_source: SessionSource::SubAgent(SubAgentSource::Other(
"agent_job:test".to_string(),
)),
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -176,7 +198,7 @@ fn image_generation_requires_feature_and_supported_model() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let supported_tools_config = ToolsConfig::new(&ToolsConfigParams {
@@ -186,7 +208,7 @@ fn image_generation_requires_feature_and_supported_model() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let auth_disallowed_tools_config = ToolsConfig::new(&ToolsConfigParams {
@@ -196,7 +218,7 @@ fn image_generation_requires_feature_and_supported_model() {
image_generation_tool_auth_allowed: false,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let unsupported_tools_config = ToolsConfig::new(&ToolsConfigParams {
@@ -206,7 +228,7 @@ fn image_generation_requires_feature_and_supported_model() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
assert!(!default_tools_config.image_gen_tool);
+44 -44
View File
@@ -26,11 +26,11 @@ use codex_protocol::config_types::WebSearchConfig;
use codex_protocol::config_types::WebSearchMode;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::VIEW_IMAGE_TOOL_NAME;
use codex_protocol::openai_models::InputModality;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::WebSearchToolType;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use pretty_assertions::assert_eq;
@@ -57,7 +57,7 @@ fn test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -160,7 +160,7 @@ fn test_build_specs_collab_tools_enabled() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -200,7 +200,7 @@ fn test_build_specs_multi_agent_v2_uses_task_names_and_hides_resume() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -343,7 +343,7 @@ fn test_build_specs_enable_fanout_enables_agent_jobs_and_collab_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -378,7 +378,7 @@ fn view_image_tool_omits_detail_without_original_detail_support() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -408,7 +408,7 @@ fn view_image_tool_includes_detail_with_original_detail_support() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -446,7 +446,7 @@ fn disabled_environment_omits_environment_backed_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
})
.with_has_environment(/*has_environment*/ false);
@@ -486,7 +486,7 @@ fn test_build_specs_agent_job_worker_tools_enabled() {
session_source: SessionSource::SubAgent(SubAgentSource::Other(
"agent_job:test".to_string(),
)),
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -523,7 +523,7 @@ fn request_user_input_description_reflects_default_mode_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -546,7 +546,7 @@ fn request_user_input_description_reflects_default_mode_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -574,7 +574,7 @@ fn request_permissions_requires_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -594,7 +594,7 @@ fn request_permissions_requires_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -623,7 +623,7 @@ fn request_permissions_tool_is_independent_from_additional_permissions() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -649,7 +649,7 @@ fn js_repl_requires_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -683,7 +683,7 @@ fn js_repl_enabled_adds_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -714,7 +714,7 @@ fn image_generation_tools_require_feature_and_supported_model() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (default_tools, _) = build_specs(
@@ -737,7 +737,7 @@ fn image_generation_tools_require_feature_and_supported_model() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (supported_tools, _) = build_specs(
@@ -763,7 +763,7 @@ fn image_generation_tools_require_feature_and_supported_model() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -793,7 +793,7 @@ fn web_search_mode_cached_sets_external_web_access_false() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -829,7 +829,7 @@ fn web_search_mode_live_sets_external_web_access_true() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -878,7 +878,7 @@ fn web_search_config_is_forwarded_to_tool_spec() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
})
.with_web_search_config(Some(web_search_config.clone()));
@@ -920,7 +920,7 @@ fn web_search_tool_type_text_and_image_sets_search_content_types() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -955,7 +955,7 @@ fn mcp_resource_tools_are_hidden_without_mcp_servers() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -986,7 +986,7 @@ fn mcp_resource_tools_are_included_when_mcp_servers_are_present() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1020,7 +1020,7 @@ fn test_parallel_support_flags() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1047,7 +1047,7 @@ fn test_test_model_info_includes_sync_tool() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1073,7 +1073,7 @@ fn test_build_specs_mcp_tools_converted() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Live),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1165,7 +1165,7 @@ fn test_build_specs_mcp_namespace_description_falls_back_when_missing() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1205,7 +1205,7 @@ fn test_build_specs_mcp_tools_sorted_by_name() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1255,7 +1255,7 @@ fn search_tool_description_lists_each_mcp_source_once() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1357,7 +1357,7 @@ fn search_tool_requires_model_capability_and_enabled_feature() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1377,7 +1377,7 @@ fn search_tool_requires_model_capability_and_enabled_feature() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1395,7 +1395,7 @@ fn search_tool_requires_model_capability_and_enabled_feature() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs(
@@ -1420,7 +1420,7 @@ fn search_tool_registers_for_deferred_dynamic_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let dynamic_tools = vec![
@@ -1509,7 +1509,7 @@ fn tool_suggest_is_not_registered_without_feature_flag() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs_with_discoverable_tools(
@@ -1549,7 +1549,7 @@ fn tool_suggest_can_be_registered_without_search_tool() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
let (tools, _) = build_specs_with_discoverable_tools(
@@ -1595,7 +1595,7 @@ fn tool_suggest_description_lists_discoverable_tools() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1697,7 +1697,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_namespaced_sample() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1750,7 +1750,7 @@ fn code_mode_preserves_nullable_and_literal_mcp_input_shapes() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1833,7 +1833,7 @@ fn code_mode_augments_builtin_tool_descriptions_with_typed_sample() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1869,7 +1869,7 @@ fn code_mode_only_exec_description_includes_full_nested_tool_details() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -1906,7 +1906,7 @@ fn code_mode_exec_description_omits_nested_tool_details_when_not_code_mode_only(
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
@@ -2063,7 +2063,7 @@ fn code_mode_augments_mcp_tool_descriptions_with_structured_output_sample() {
image_generation_tool_auth_allowed: true,
web_search_mode: Some(WebSearchMode::Cached),
session_source: SessionSource::Cli,
sandbox_policy: &SandboxPolicy::DangerFullAccess,
permission_profile: &PermissionProfile::Disabled,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
});
+13 -22
View File
@@ -72,15 +72,15 @@ impl App {
"Failed to carry forward approval policy override: {err}"
));
}
if let Some(policy) = self.runtime_sandbox_policy_override.as_ref() {
if let Err(err) = config.permissions.sandbox_policy.set(policy.clone()) {
tracing::warn!(%err, "failed to carry forward sandbox policy override");
self.chat_widget.add_error_message(format!(
"Failed to carry forward sandbox policy override: {err}"
));
} else {
sync_runtime_permissions_from_legacy_sandbox_policy(config);
}
if let Some(policy) = self.runtime_sandbox_policy_override.as_ref()
&& let Err(err) = config
.permissions
.set_legacy_sandbox_policy(policy.clone(), config.cwd.as_path())
{
tracing::warn!(%err, "failed to carry forward sandbox policy override");
self.chat_widget.add_error_message(format!(
"Failed to carry forward sandbox policy override: {err}"
));
}
}
@@ -113,13 +113,15 @@ impl App {
user_message_prefix: &str,
log_message: &str,
) -> bool {
if let Err(err) = config.permissions.sandbox_policy.set(policy) {
if let Err(err) = config
.permissions
.set_legacy_sandbox_policy(policy, config.cwd.as_path())
{
tracing::warn!(error = %err, "{log_message}");
self.chat_widget
.add_error_message(format!("{user_message_prefix}: {err}"));
return false;
}
sync_runtime_permissions_from_legacy_sandbox_policy(config);
true
}
@@ -543,17 +545,6 @@ impl App {
}
}
fn sync_runtime_permissions_from_legacy_sandbox_policy(config: &mut Config) {
let sandbox_policy = config.permissions.sandbox_policy.get();
config.permissions.file_system_sandbox_policy =
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
sandbox_policy,
&config.cwd,
);
config.permissions.network_sandbox_policy =
codex_protocol::permissions::NetworkSandboxPolicy::from(sandbox_policy);
}
#[cfg(test)]
mod tests {
use super::*;
+46 -33
View File
@@ -2119,20 +2119,6 @@ impl ChatWidget {
display: SessionConfiguredDisplay,
fork_parent_title: Option<String>,
) {
let (file_system_sandbox_policy, network_sandbox_policy) = match event
.permission_profile
.as_ref()
{
Some(permission_profile) => permission_profile.to_runtime_permissions(),
None => (
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
&event.sandbox_policy,
&event.cwd,
),
codex_protocol::permissions::NetworkSandboxPolicy::from(&event.sandbox_policy),
),
};
self.last_agent_markdown = None;
self.agent_turn_markdowns.clear();
self.visible_user_turn_count = 0;
@@ -2160,18 +2146,52 @@ impl ChatWidget {
self.config.permissions.approval_policy =
Constrained::allow_only(event.approval_policy);
}
if let Err(err) = self
.config
.permissions
.sandbox_policy
.set(event.sandbox_policy.clone())
{
tracing::warn!(%err, "failed to sync sandbox_policy from SessionConfigured");
let permission_sync = match event.permission_profile.clone() {
Some(permission_profile) => self
.config
.permissions
.set_permission_profile(permission_profile, event.cwd.as_path()),
None => self
.config
.permissions
.set_legacy_sandbox_policy(event.sandbox_policy.clone(), event.cwd.as_path()),
};
if let Err(err) = permission_sync {
tracing::warn!(%err, "failed to sync permissions from SessionConfigured");
self.config.permissions.sandbox_policy =
Constrained::allow_only(event.sandbox_policy.clone());
match event.permission_profile.clone() {
Some(permission_profile) => {
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
self.config.permissions.permission_profile =
Constrained::allow_only(permission_profile);
self.config.permissions.file_system_sandbox_policy = file_system_sandbox_policy;
self.config.permissions.network_sandbox_policy = network_sandbox_policy;
}
None => {
self.config.permissions.file_system_sandbox_policy =
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
&event.sandbox_policy,
&event.cwd,
);
self.config.permissions.network_sandbox_policy =
codex_protocol::permissions::NetworkSandboxPolicy::from(
&event.sandbox_policy,
);
let permission_profile =
codex_protocol::models::PermissionProfile::from_runtime_permissions_with_enforcement(
codex_protocol::models::SandboxEnforcement::from_legacy_sandbox_policy(
&event.sandbox_policy,
),
&self.config.permissions.file_system_sandbox_policy,
self.config.permissions.network_sandbox_policy,
);
self.config.permissions.permission_profile =
Constrained::allow_only(permission_profile);
}
}
}
self.config.permissions.file_system_sandbox_policy = file_system_sandbox_policy;
self.config.permissions.network_sandbox_policy = network_sandbox_policy;
self.config.approvals_reviewer = event.approvals_reviewer;
self.status_line_project_root_name_cache = None;
let forked_from_id = event.forked_from_id;
@@ -9788,16 +9808,9 @@ impl ChatWidget {
/// Set the sandbox policy in the widget's config copy.
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
pub(crate) fn set_sandbox_policy(&mut self, policy: SandboxPolicy) -> ConstraintResult<()> {
self.config.permissions.sandbox_policy.set(policy)?;
let sandbox_policy = self.config.permissions.sandbox_policy.get();
self.config.permissions.file_system_sandbox_policy =
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
sandbox_policy,
&self.config.cwd,
);
self.config.permissions.network_sandbox_policy =
codex_protocol::permissions::NetworkSandboxPolicy::from(sandbox_policy);
Ok(())
self.config
.permissions
.set_legacy_sandbox_policy(policy, self.config.cwd.as_path())
}
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
@@ -258,7 +258,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
.expect("set sandbox policy");
chat.config.cwd = test_path_buf("/home/user/main").abs();
let expected_sandbox = SandboxPolicy::new_read_only_policy();
let legacy_fallback_sandbox = SandboxPolicy::new_read_only_policy();
let expected_cwd = test_path_buf("/home/user/sub-agent").abs();
let expected_file_system_policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
@@ -279,6 +279,9 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
&expected_file_system_policy,
NetworkSandboxPolicy::Restricted,
);
let expected_sandbox = expected_permission_profile
.to_legacy_sandbox_policy(expected_cwd.as_path())
.expect("permission profile should project to legacy sandbox policy");
let configured = codex_protocol::protocol::SessionConfiguredEvent {
session_id: ThreadId::new(),
forked_from_id: None,
@@ -288,7 +291,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
service_tier: None,
approval_policy: AskForApproval::Never,
approvals_reviewer: ApprovalsReviewer::User,
sandbox_policy: expected_sandbox.clone(),
sandbox_policy: legacy_fallback_sandbox,
permission_profile: Some(expected_permission_profile.clone()),
cwd: expected_cwd.clone(),
reasoning_effort: Some(ReasoningEffortConfig::default()),