diff --git a/codex-rs/config/src/config_requirements.rs b/codex-rs/config/src/config_requirements.rs index 437089ba38..5419d399df 100644 --- a/codex-rs/config/src/config_requirements.rs +++ b/codex-rs/config/src/config_requirements.rs @@ -505,13 +505,13 @@ impl From for NetworkConstraints { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct FilesystemRequirementsToml { pub deny_read: Option>, - pub allow_limited_git_writes: Option, + pub allow_git: Option, } #[derive(Deserialize)] struct RawFilesystemRequirementsToml { deny_read: Option>, - allow_limited_git_writes: Option, + allow_git: Option, description: Option, extends: Option, workspace_roots: Option, @@ -527,7 +527,7 @@ impl<'de> Deserialize<'de> for FilesystemRequirementsToml { let raw = RawFilesystemRequirementsToml::deserialize(deserializer)?; let RawFilesystemRequirementsToml { deny_read, - allow_limited_git_writes, + allow_git, description, extends, workspace_roots, @@ -548,7 +548,7 @@ impl<'de> Deserialize<'de> for FilesystemRequirementsToml { Ok(Self { deny_read, - allow_limited_git_writes, + allow_git, }) } } @@ -565,7 +565,7 @@ pub struct PermissionsRequirementsToml { #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct FilesystemConstraints { pub deny_read: Vec, - pub allow_limited_git_writes: Option, + pub allow_git: Option, } impl From for FilesystemConstraints { @@ -575,7 +575,7 @@ impl From for FilesystemConstraints { }; Self { deny_read: filesystem.deny_read.unwrap_or_default(), - allow_limited_git_writes: filesystem.allow_limited_git_writes, + allow_git: filesystem.allow_git, } } } @@ -2003,7 +2003,7 @@ allowed_approvals_reviewers = ["user"] r#" [permissions.filesystem] deny_read = [{deny_read_0:?}, {deny_read_1:?}] - allow_limited_git_writes = true + allow_git = true "# ); @@ -2018,7 +2018,7 @@ allowed_approvals_reviewers = ["user"] AbsolutePathBuf::from_absolute_path(deny_read_0)?.into(), AbsolutePathBuf::from_absolute_path(deny_read_1)?.into(), ], - allow_limited_git_writes: Some(true), + allow_git: Some(true), }, RequirementSource::Unknown, )) @@ -2047,7 +2047,7 @@ allowed_approvals_reviewers = ["user"] FilesystemDenyReadPattern::from_input("./private/**/*.txt") .expect("normalize glob pattern"), ], - allow_limited_git_writes: None, + allow_git: None, }, RequirementSource::Unknown, )) diff --git a/codex-rs/core/README.md b/codex-rs/core/README.md index 16c78e6005..915bfc47c5 100644 --- a/codex-rs/core/README.md +++ b/codex-rs/core/README.md @@ -13,7 +13,7 @@ Expects `/usr/bin/sandbox-exec` to be present. When using the workspace-write sandbox policy, the Seatbelt profile allows writes under the configured writable roots while keeping `.git` (directory or pointer file), the resolved `gitdir:` target, and `.codex` read-only by -default. When `allow_limited_git_writes` is enabled, Git metadata writes are allowed, +default. When `allow_git` is enabled, Git metadata writes are allowed, but `.git/config`, `.git/hooks`, and resolved worktree gitdir config/hooks remain read-only. diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 6dd85dffc2..e9b72edfc9 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -886,6 +886,10 @@ }, "FilesystemPermissionsToml": { "properties": { + "allow_git": { + "default": false, + "type": "boolean" + }, "glob_scan_max_depth": { "description": "Optional maximum depth for expanding unreadable glob patterns on platforms that snapshot glob matches before sandbox startup.", "format": "uint", diff --git a/codex-rs/core/src/config/config_loader_tests.rs b/codex-rs/core/src/config/config_loader_tests.rs index 27ca34906d..eac4fb4ced 100644 --- a/codex-rs/core/src/config/config_loader_tests.rs +++ b/codex-rs/core/src/config/config_loader_tests.rs @@ -1548,7 +1548,7 @@ extends = ":workspace" let requirements = format!( r#" [permissions.filesystem] -allow_limited_git_writes = true +allow_git = true {managed_profile}"# ); @@ -1600,11 +1600,11 @@ default_permissions = "local" extends = ":workspace" [permissions.local.filesystem] -allow_limited_git_writes = true +allow_git = true "#, r#" [permissions.filesystem] -allow_limited_git_writes = false +allow_git = false "#, None, ) @@ -1642,7 +1642,7 @@ extends = ":workspace" "#, r#" [permissions.filesystem] -allow_limited_git_writes = true +allow_git = true "#, None, ) @@ -1678,7 +1678,7 @@ exclude_slash_tmp = true "#, r#" [permissions.filesystem] -allow_limited_git_writes = true +allow_git = true "#, None, ) @@ -1716,7 +1716,7 @@ async fn system_filesystem_requirements_reject_unrestricted_profile_selection() r#"sandbox_mode = "danger-full-access""#, r#" [permissions.filesystem] -allow_limited_git_writes = true +allow_git = true "#, None, ) diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 8e9a762c7b..f76eac3a28 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -900,6 +900,7 @@ strip_request_headers = ["authorization"] }), filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([ ( ":minimal".to_string(), @@ -1117,6 +1118,7 @@ async fn permissions_profiles_proxy_policy_does_not_start_managed_network_proxy_ workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1167,6 +1169,7 @@ async fn permissions_profiles_proxy_policy_starts_managed_network_proxy() -> std workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1323,6 +1326,7 @@ async fn network_proxy_feature_matrix_preserves_sandbox_network_semantics() -> s workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1476,6 +1480,7 @@ async fn network_proxy_feature_uses_profile_network_proxy_settings() -> std::io: workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1540,6 +1545,7 @@ enabled = false workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1591,6 +1597,7 @@ async fn permissions_profiles_network_disabled_by_default_does_not_start_proxy() workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1640,6 +1647,7 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std:: workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([ ( ":minimal".to_string(), @@ -1700,7 +1708,7 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std:: &SandboxPolicy::WorkspaceWrite { writable_roots: vec![], network_access: false, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: true, } @@ -1745,6 +1753,7 @@ async fn default_permissions_extended_profile_preserves_parent_metadata() -> std workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -1973,7 +1982,7 @@ async fn permission_profile_override_keeps_memories_root_out_of_legacy_projectio &SandboxPolicy::WorkspaceWrite { writable_roots: vec![], network_access: false, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: true, } @@ -2000,6 +2009,7 @@ async fn permission_profile_override_preserves_configured_network_policy_without workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -2062,6 +2072,7 @@ async fn workspace_root_glob_none_compiles_to_filesystem_pattern_entry() -> std: workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: Some(2), + allow_git: false, entries: BTreeMap::from([( ":workspace_roots".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([ @@ -2143,6 +2154,7 @@ async fn permissions_profiles_require_default_permissions() -> std::io::Result<( workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -2277,6 +2289,7 @@ async fn workspace_profile_applies_rules_to_runtime_and_profile_workspace_roots( }), filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":workspace_roots".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([ @@ -2360,7 +2373,7 @@ async fn explicit_builtin_workspace_profile_ignores_legacy_workspace_write_setti sandbox_workspace_write: Some(SandboxWorkspaceWrite { writable_roots: vec![extra_root.path().abs()], network_access: true, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: true, }), @@ -2577,7 +2590,7 @@ async fn implicit_builtin_workspace_profile_preserves_sandbox_workspace_write_se sandbox_workspace_write: Some(SandboxWorkspaceWrite { writable_roots: vec![extra_root.clone()], network_access: true, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: false, }), @@ -2847,6 +2860,7 @@ async fn permissions_profiles_allow_direct_write_roots_outside_workspace_root() workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( external_write_path.to_string_lossy().into_owned(), FilesystemPermissionToml::Access(FileSystemAccessMode::Write), @@ -2884,7 +2898,7 @@ async fn permissions_profiles_allow_direct_write_roots_outside_workspace_root() &SandboxPolicy::WorkspaceWrite { writable_roots: vec![external_write_path], network_access: false, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: true, } @@ -2911,6 +2925,7 @@ async fn permissions_profiles_reject_nested_entries_for_non_workspace_roots() -> workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([( @@ -2974,6 +2989,7 @@ async fn permissions_profiles_allow_unknown_special_paths() -> std::io::Result<( workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":future_special_path".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -3020,6 +3036,7 @@ async fn permissions_profiles_allow_unknown_special_paths_with_nested_entries() workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":future_special_path".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([( @@ -3090,6 +3107,7 @@ async fn permissions_profiles_allow_empty_filesystem_with_warning() -> std::io:: workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::new(), }), network: None, @@ -3128,6 +3146,7 @@ async fn permissions_profiles_reject_workspace_root_parent_traversal() -> std::i workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":workspace_roots".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([( @@ -3177,6 +3196,7 @@ async fn permissions_profiles_allow_network_enablement() -> std::io::Result<()> workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":minimal".to_string(), FilesystemPermissionToml::Access(FileSystemAccessMode::Read), @@ -3606,7 +3626,7 @@ trust_level = "trusted" SandboxPolicy::WorkspaceWrite { writable_roots: vec![writable_root.clone()], network_access: false, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: true, } @@ -3647,7 +3667,7 @@ exclude_slash_tmp = true SandboxPolicy::WorkspaceWrite { writable_roots: vec![writable_root], network_access: false, - allow_limited_git_writes: false, + allow_git: false, exclude_tmpdir_env_var: true, exclude_slash_tmp: true, } diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index ab481e2e1a..9b24c52ae7 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -2150,7 +2150,7 @@ fn apply_managed_filesystem_constraints( filesystem_constraints: &codex_config::FilesystemConstraints, cwd: &Path, ) { - if filesystem_constraints.allow_limited_git_writes == Some(true) { + if filesystem_constraints.allow_git == Some(true) { file_system_sandbox_policy.apply_managed_limited_git_writes(cwd); } for deny_read in &filesystem_constraints.deny_read { @@ -3279,7 +3279,7 @@ impl Config { source: filesystem_requirements_source, }) = filesystem_requirements.as_ref() && (!filesystem_requirements.deny_read.is_empty() - || filesystem_requirements.allow_limited_git_writes.is_some()) + || filesystem_requirements.allow_git.is_some()) { let requirement_source = filesystem_requirements_source.clone(); constrained_permission_profile @@ -3699,7 +3699,7 @@ impl Config { .requirements() .filesystem .as_ref() - .and_then(|filesystem| filesystem.value.allow_limited_git_writes) + .and_then(|filesystem| filesystem.value.allow_git) } pub(crate) fn network_proxy_spec_for_active_permission_profile( diff --git a/codex-rs/core/src/config/permissions_tests.rs b/codex-rs/core/src/config/permissions_tests.rs index 77d1c9ff0f..6d46307849 100644 --- a/codex-rs/core/src/config/permissions_tests.rs +++ b/codex-rs/core/src/config/permissions_tests.rs @@ -72,6 +72,7 @@ async fn restricted_read_implicitly_allows_helper_executables() -> std::io::Resu workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::new(), }), network: None, @@ -471,6 +472,7 @@ fn compile_permission_profile_workspace_roots_resolves_enabled_entries() -> std: fn read_write_glob_warnings_skip_supported_deny_read_globs_and_trailing_subpaths() { let filesystem = FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([ ( "/tmp/**/*.log".to_string(), @@ -505,6 +507,7 @@ fn read_write_glob_warnings_skip_supported_deny_read_globs_and_trailing_subpaths fn unreadable_globstar_warning_is_suppressed_when_scan_depth_is_configured() { let filesystem = FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":workspace_roots".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([ @@ -557,7 +560,7 @@ fn workspace_parent_uses_profile_limited_git_write_setting() -> std::io::Result< workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, - allow_limited_git_writes: true, + allow_git: true, entries: BTreeMap::new(), }), network: None, @@ -598,6 +601,7 @@ fn read_write_trailing_glob_suffix_compiles_as_subpath() -> std::io::Result<()> workspace_roots: None, filesystem: Some(FilesystemPermissionsToml { glob_scan_max_depth: None, + allow_git: false, entries: BTreeMap::from([( ":workspace_roots".to_string(), FilesystemPermissionToml::Scoped(BTreeMap::from([( diff --git a/codex-rs/tui/src/debug_config.rs b/codex-rs/tui/src/debug_config.rs index fff9e5cf7a..34ad4fb47c 100644 --- a/codex-rs/tui/src/debug_config.rs +++ b/codex-rs/tui/src/debug_config.rs @@ -242,9 +242,9 @@ fn render_debug_config_lines(stack: &ConfigLayerStack) -> Vec> { } if let Some(filesystem) = requirements.filesystem.as_ref() { - if let Some(allow_limited_git_writes) = filesystem.value.allow_limited_git_writes { + if let Some(allow_limited_git_writes) = filesystem.value.allow_git { requirement_lines.push(requirement_line( - "permissions.filesystem.allow_limited_git_writes", + "permissions.filesystem.allow_git", allow_limited_git_writes.to_string(), Some(&filesystem.source), )); @@ -696,7 +696,7 @@ mod tests { filesystem: Some(Sourced::new( FilesystemConstraints { deny_read: vec![denied_path.clone().into()], - allow_limited_git_writes: Some(true), + allow_git: Some(true), }, RequirementSource::SystemRequirementsToml { file: requirements_file.clone(), @@ -791,7 +791,7 @@ mod tests { assert!( rendered.contains( format!( - "permissions.filesystem.allow_limited_git_writes: true (source: {})", + "permissions.filesystem.allow_git: true (source: {})", requirements_file.as_path().display() ) .as_str(),