Skip missing paths in filesystem sandbox entries (#34598)

## Why

Default read-only protections for project metadata should apply when paths such
as `.git`, `.agents`, and `.codex` exist, without causing sandbox setup to
materialize missing paths as ACL targets.

## What changed

- Add an optional `missing_path_behavior` to filesystem sandbox entries and
  mark default project-metadata protections with `skip`.
- Preserve the behavior through permission transforms and exec/MCP protocol
  serialization while keeping existing path wire variants stable.
- Ignore skip-missing entries when projecting configuration or Windows sandbox
  overrides, while retaining explicit metadata carveouts.

## Testing

- Cover protocol round trips for path and special-path entries.
- Verify default metadata protections and Windows explicit carveout handling.

GitOrigin-RevId: 6df13dadacdd131c44aab9f15a967c81051355c1
This commit is contained in:
iceweasel-oai
2026-07-21 19:17:18 +00:00
committed by copyberry
parent ee71c4a90f
commit 87f71e35b8
63 changed files with 825 additions and 132 deletions
@@ -368,6 +368,7 @@ impl TryFrom<FileSystemSandboxEntry> for CoreFileSystemSandboxEntry {
Ok(Self {
path: value.path.try_into()?,
access: value.access.to_core(),
missing_path_behavior: None,
})
}
}
@@ -591,12 +591,14 @@ fn additional_file_system_permissions_preserves_canonical_entries() {
value: CoreFileSystemSpecialPath::Root,
},
access: CoreFileSystemAccessMode::Write,
missing_path_behavior: None,
},
CoreFileSystemSandboxEntry {
path: CoreFileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: CoreFileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: NonZeroUsize::new(2),
@@ -3071,6 +3071,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -3118,6 +3119,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -520,12 +520,14 @@ mod thread_processor_behavior_tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: cwd.clone() },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "/tmp/project/**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
+57
View File
@@ -148,6 +148,7 @@ mod tests {
use codex_exec_server::EnvironmentManager;
use codex_utils_path_uri::LegacyAppPathString;
use pretty_assertions::assert_eq;
use serde_json::Value;
use super::*;
@@ -192,6 +193,62 @@ mod tests {
}
}
#[test]
fn sandbox_state_serializes_skip_missing_entries_as_missing_path_behavior() {
let sandbox_cwd = PathUri::from_host_native_path(
std::env::current_dir().expect("current directory should be available"),
)
.expect("current directory should convert to a URI");
let sandbox_state = SandboxState {
permission_profile: PermissionProfile::workspace_write(),
codex_linux_sandbox_exe: None,
sandbox_cwd,
use_legacy_landlock: false,
};
let serialized = serde_json::to_value(&sandbox_state).expect("serialize sandbox state");
let serialized_text = serde_json::to_string(&serialized).expect("serialize JSON text");
assert!(
!serialized_text.contains("generated_default_path"),
"MCP sandbox metadata must preserve FileSystemPath's stable wire variants"
);
assert!(
!serialized_text.contains("generated_default_special"),
"MCP sandbox metadata must preserve FileSystemPath's stable wire variants"
);
let entries = serialized
.pointer("/permissionProfile/file_system/entries")
.and_then(Value::as_array)
.expect("workspace-write profile should contain filesystem entries");
let skip_missing_entries = entries
.iter()
.filter(|entry| {
entry.get("missing_path_behavior").and_then(Value::as_str) == Some("skip")
})
.collect::<Vec<_>>();
assert!(
!skip_missing_entries.is_empty(),
"skip-missing entries should be represented as optional missing_path_behavior"
);
assert!(
skip_missing_entries.iter().all(|entry| {
matches!(
entry.pointer("/path/type").and_then(Value::as_str),
Some("path" | "special")
)
}),
"skip-missing entries should use the stable path/special variants"
);
let deserialized: SandboxState =
serde_json::from_value(serialized).expect("deserialize sandbox state");
assert_eq!(
deserialized.permission_profile,
sandbox_state.permission_profile
);
}
#[test]
fn local_stdio_requires_local_stdio_availability() {
let runtime_context = McpRuntimeContext::new(
+18
View File
@@ -2037,18 +2037,21 @@ async fn default_permissions_profile_populates_runtime_sandbox_policy() -> std::
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: cwd_root.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: cwd_root.join("docs"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]),
);
@@ -2295,12 +2298,14 @@ async fn permission_profile_override_keeps_memories_root_out_of_legacy_projectio
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
@@ -2461,6 +2466,7 @@ async fn workspace_root_glob_none_compiles_to_filesystem_pattern_entry() -> std:
pattern: expected_pattern,
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
})
);
}
@@ -2800,6 +2806,7 @@ async fn default_permissions_profile_can_extend_builtin_workspace() -> std::io::
value: FileSystemSpecialPath::SlashTmp,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}
)),
"expected profile extending :workspace to keep inherited :slash_tmp writes, policy: {policy:?}"
@@ -2812,6 +2819,7 @@ async fn default_permissions_profile_can_extend_builtin_workspace() -> std::io::
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}
)),
"expected child :tmpdir read entry to replace the inherited write entry, policy: {policy:?}"
@@ -2824,6 +2832,7 @@ async fn default_permissions_profile_can_extend_builtin_workspace() -> std::io::
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}
)),
"expected inherited :tmpdir write entry to be removed, policy: {policy:?}"
@@ -3446,6 +3455,7 @@ async fn permissions_profiles_allow_unknown_special_paths() -> std::io::Result<(
),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
);
assert_eq!(
@@ -3492,6 +3502,7 @@ async fn permissions_profiles_allow_unknown_special_paths_with_nested_entries()
value: FileSystemSpecialPath::unknown(":future_special_path", Some("docs".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
);
assert!(
@@ -4247,6 +4258,7 @@ exclude_slash_tmp = true
.contains(&FileSystemSandboxEntry {
path: FileSystemPath::Path { path: cwd.abs() },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
})
);
assert!(
@@ -4257,6 +4269,7 @@ exclude_slash_tmp = true
path: extra_root.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
})
);
for subpath in [".git", ".agents", ".codex"] {
@@ -4271,6 +4284,9 @@ exclude_slash_tmp = true
),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: Some(
codex_protocol::permissions::FileSystemSandboxEntryMissingPathBehavior::Skip,
),
}),
"case `{name}` should materialize `{subpath}` for the runtime workspace \
root"
@@ -9993,12 +10009,14 @@ async fn permission_profile_override_preserves_split_write_roots() -> std::io::R
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: outside_root.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
+2
View File
@@ -2410,6 +2410,7 @@ fn apply_managed_filesystem_constraints(
pattern: deny_read.as_str().to_string(),
},
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
missing_path_behavior: None,
}
} else {
let Ok(path) = AbsolutePathBuf::try_from(deny_read.as_str()) else {
@@ -2418,6 +2419,7 @@ fn apply_managed_filesystem_constraints(
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path },
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
missing_path_behavior: None,
}
};
if !file_system_sandbox_policy
+7
View File
@@ -236,6 +236,10 @@ fn insert_filesystem_permission_toml(
entries: &mut BTreeMap<String, FilesystemPermissionToml>,
entry: FileSystemSandboxEntry,
) {
if entry.skips_missing_path() {
return;
}
match entry.path {
FileSystemPath::Path { path } => {
entries.insert(
@@ -526,6 +530,7 @@ fn compile_filesystem_permission(
entries.push(FileSystemSandboxEntry {
path: compile_filesystem_access_path(path, *access, startup_warnings)?,
access: *access,
missing_path_behavior: None,
});
}
FilesystemPermissionToml::Scoped(scoped_entries) => {
@@ -544,6 +549,7 @@ fn compile_filesystem_permission(
pattern: compile_scoped_filesystem_pattern(path, subpath, *access)?,
},
access: *access,
missing_path_behavior: None,
};
entries.push(entry);
} else {
@@ -551,6 +557,7 @@ fn compile_filesystem_permission(
entries.push(FileSystemSandboxEntry {
path: compile_scoped_filesystem_path(path, subpath, startup_warnings)?,
access: *access,
missing_path_behavior: None,
});
}
}
@@ -577,6 +577,7 @@ fn read_write_trailing_glob_suffix_compiles_as_subpath() -> std::io::Result<()>
value: FileSystemSpecialPath::project_roots(Some("docs".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
"trailing /** should compile as a subtree path instead of a glob pattern"
);
@@ -152,18 +152,21 @@ fn workspace_write_permission_profile_with_private_denials() -> PermissionProfil
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some("private".to_string())),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: project_roots_glob_pattern(Path::new("private/**")),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
+5
View File
@@ -1182,12 +1182,14 @@ fn managed_cwd_write_profile_has_filesystem_restrictions() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -1208,6 +1210,7 @@ fn managed_unresolvable_write_profile_has_filesystem_restrictions() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
@@ -1217,6 +1220,7 @@ fn managed_unresolvable_write_profile_has_filesystem_restrictions() {
),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -1237,6 +1241,7 @@ fn managed_full_disk_write_profile_has_no_filesystem_restrictions() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_sandbox_policy,
@@ -142,12 +142,14 @@ fn writable_windows_policy_without_sandbox_backend_still_requires_approval() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
+72
View File
@@ -441,6 +441,7 @@ fn windows_restricted_token_rejects_managed_root_write_profiles() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -512,6 +513,7 @@ fn windows_elevated_allows_split_restricted_read_policies() {
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs },
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -543,6 +545,7 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -550,6 +553,7 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() {
.expect("absolute docs"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -582,6 +586,7 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -589,6 +594,7 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() {
.expect("absolute docs"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -624,6 +630,7 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -632,10 +639,12 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs.clone() },
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -679,6 +688,7 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -687,10 +697,12 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: blocked },
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -725,6 +737,7 @@ fn windows_elevated_supports_split_restricted_read_roots() {
.expect("absolute docs"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -761,6 +774,7 @@ fn windows_elevated_supports_split_write_read_carveouts() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -769,6 +783,7 @@ fn windows_elevated_supports_split_write_read_carveouts() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -776,6 +791,7 @@ fn windows_elevated_supports_split_write_read_carveouts() {
.expect("absolute docs"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -803,6 +819,52 @@ fn windows_elevated_supports_split_write_read_carveouts() {
);
}
#[cfg(target_os = "windows")]
#[test]
fn windows_workspace_defaults_do_not_hide_explicit_metadata_carveouts() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
let cwd = temp_dir.path().canonicalize().expect("canonical cwd").abs();
let default_profile = PermissionProfile::workspace_write();
let default_overrides = resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&default_profile,
&cwd,
/*use_windows_elevated_backend*/ true,
)
.expect("resolve workspace defaults");
assert!(
default_overrides.is_none_or(|overrides| overrides.additional_deny_write_paths.is_empty())
);
for name in codex_protocol::permissions::PROTECTED_METADATA_PATH_NAMES {
let (mut explicit_policy, network_policy) = default_profile.to_runtime_permissions();
explicit_policy
.entries
.push(codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
value: codex_protocol::permissions::FileSystemSpecialPath::project_roots(Some(
(*name).into(),
)),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
});
let explicit_profile =
PermissionProfile::from_runtime_permissions(&explicit_policy, network_policy);
let overrides = resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&explicit_profile,
&cwd,
/*use_windows_elevated_backend*/ true,
)
.expect("resolve explicit metadata carveout")
.expect("explicit metadata carveout needs an override");
assert_eq!(overrides.additional_deny_write_paths, vec![cwd.join(name)]);
}
}
#[test]
fn windows_elevated_supports_unreadable_split_carveouts() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
@@ -815,6 +877,7 @@ fn windows_elevated_supports_unreadable_split_carveouts() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -823,6 +886,7 @@ fn windows_elevated_supports_unreadable_split_carveouts() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -830,6 +894,7 @@ fn windows_elevated_supports_unreadable_split_carveouts() {
.expect("absolute blocked"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -874,6 +939,7 @@ fn windows_elevated_supports_unreadable_globs() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -882,12 +948,14 @@ fn windows_elevated_supports_unreadable_globs() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -927,6 +995,7 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -935,6 +1004,7 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -942,6 +1012,7 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
.expect("absolute docs"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -949,6 +1020,7 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
.expect("absolute nested"),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
+3
View File
@@ -473,18 +473,21 @@ async fn build_guardian_prompt_includes_parent_turn_denied_reads() -> anyhow::Re
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_root.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: denied_glob.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
+5
View File
@@ -230,12 +230,14 @@ fn explicit_unreadable_paths_prevent_auto_approval_for_external_sandbox() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: blocked_absolute,
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -278,12 +280,14 @@ fn explicit_read_only_subpaths_prevent_auto_approval_for_external_sandbox() {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: docs_absolute,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -327,6 +331,7 @@ fn missing_project_dot_codex_config_requires_approval() {
path: cwd.join(".codex"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
});
assert!(!is_write_patch_constrained_to_writable_paths(
+2
View File
@@ -98,6 +98,7 @@ fn root_write_managed_profile_with_enabled_network_is_untagged() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
},
@@ -148,6 +149,7 @@ fn profile_policy_tag_reports_closest_legacy_mode() {
path: writable_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
},
NetworkSandboxPolicy::Restricted,
+9
View File
@@ -4606,10 +4606,12 @@ async fn session_configuration_apply_preserves_profile_file_system_policy_on_cwd
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs_dir },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy);
@@ -4653,6 +4655,7 @@ async fn session_configuration_apply_permission_profile_preserves_existing_deny_
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
};
let mut existing_file_system_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
@@ -4716,6 +4719,7 @@ async fn session_configuration_apply_permission_profile_accepts_direct_write_roo
path: external_write_path.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_sandbox_policy,
@@ -4965,12 +4969,14 @@ async fn session_configuration_apply_preserves_absolute_cwd_write_root_on_cwd_up
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: original_cwd.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
session_configuration
@@ -6208,6 +6214,7 @@ async fn request_permissions_tool_resolves_relative_paths_against_selected_envir
path: environment_cwd.join("relative.txt"),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -6294,6 +6301,7 @@ async fn request_permissions_response_materializes_session_cwd_grants_before_rec
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -8858,6 +8866,7 @@ fn file_system_policy_with_unreadable_glob(turn_context: &TurnContext) -> FileSy
pattern: format!("{cwd_display}/**/*.env"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
policy
}
+2
View File
@@ -444,12 +444,14 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -2283,6 +2283,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() {
pattern: "**/.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
let expected_network_sandbox_policy = NetworkSandboxPolicy::from(&expected_sandbox);
let expected_permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
@@ -69,6 +69,7 @@ fn read_only_file_system_sandbox_policy() -> FileSystemSandboxPolicy {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}])
}
@@ -79,12 +80,14 @@ fn denied_read_file_system_sandbox_policy() -> FileSystemSandboxPolicy {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
])
}
@@ -299,12 +302,14 @@ fn shell_request_escalation_execution_is_explicit() {
path: AbsolutePathBuf::from_absolute_path("/tmp/original/output").unwrap(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::from_absolute_path("/tmp/secret").unwrap(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let network_sandbox_policy = NetworkSandboxPolicy::Restricted;
@@ -151,6 +151,7 @@ fn deny_read_blocks_explicit_escalation_and_policy_bypass() {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}]);
assert_eq!(
@@ -140,12 +140,14 @@ fn workspace_write_with_read_only_root(read_only_root: AbsolutePathBuf) -> Permi
path: read_only_root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
PermissionProfile::from_runtime_permissions(
@@ -162,12 +164,14 @@ fn workspace_write_with_unreadable_path(unreadable_path: AbsolutePathBuf) -> Per
path: unreadable_path,
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
PermissionProfile::from_runtime_permissions(
+1
View File
@@ -3588,6 +3588,7 @@ allow_local_binding = true
pattern: format!("{}/**/*.env", test.config.cwd.as_path().display()),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
assert!(
file_system_sandbox_policy.has_denied_read_restrictions(),
@@ -115,6 +115,7 @@ async fn extension_tool_receives_turn_environment_sandbox() -> Result<()> {
path: denied_path.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_sandbox_policy,
+4
View File
@@ -1129,6 +1129,7 @@ fn read_only_sandbox(readable_root: PathBuf) -> FileSystemSandboxContext {
path: readable_root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
NetworkSandboxPolicy::Restricted,
))
@@ -1142,6 +1143,7 @@ fn workspace_write_sandbox(writable_root: PathBuf) -> FileSystemSandboxContext {
path: writable_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]),
NetworkSandboxPolicy::Restricted,
))
@@ -1361,12 +1363,14 @@ async fn remote_exec_materializes_target_roots_before_sandbox_selection() -> Res
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
+1
View File
@@ -453,6 +453,7 @@ async fn shell_command_enforces_glob_deny_read_policy() -> Result<()> {
pattern: format!("{}/**/*.env", config.cwd.as_path().display()),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
config
.permissions
@@ -3095,6 +3095,7 @@ async fn unified_exec_enforces_glob_deny_read_policy() -> Result<()> {
pattern: format!("{}/**/*.env", config.cwd.as_path().display()),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
config
.permissions
@@ -337,6 +337,7 @@ fn permission_profile_from_toml(profile: &str) -> Result<PermissionProfile> {
Ok(FileSystemSandboxEntry {
path,
access: *access,
missing_path_behavior: None,
})
})
.collect::<Result<Vec<_>>>()?;
+1
View File
@@ -532,6 +532,7 @@ async fn view_image_tool_applies_local_sandbox_read_denies() -> anyhow::Result<(
path: denied_path.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_sandbox_policy,
@@ -134,24 +134,28 @@ async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> a
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: future_secret,
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -197,6 +201,59 @@ async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> a
Ok(())
}
#[tokio::test]
#[serial(codex_home)]
async fn windows_elevated_does_not_create_missing_workspace_metadata() -> anyhow::Result<()> {
let codex_home =
codex_home_for_windows_sandbox_test("windows-elevated-missing-metadata-codex-home")?;
let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str());
stage_windows_sandbox_helpers()?;
let workspace = TempDir::new()?;
let cwd = dunce::canonicalize(workspace.path())?.abs();
let permission_profile = PermissionProfile::workspace_write()
.materialize_project_roots_with_workspace_roots(std::slice::from_ref(&cwd));
let output = process_exec_tool_call(
ExecParams {
command: vec![
"cmd.exe".to_string(),
"/D".to_string(),
"/C".to_string(),
"echo sandbox-ok".to_string(),
],
cwd: cwd.clone(),
expiration: 10_000.into(),
capture_policy: ExecCapturePolicy::ShellTool,
env: HashMap::new(),
network: None,
network_environment_id: None,
sandbox_permissions: SandboxPermissions::UseDefault,
windows_sandbox_level: WindowsSandboxLevel::Elevated,
windows_sandbox_private_desktop: false,
justification: None,
arg0: None,
},
&permission_profile,
&cwd,
std::slice::from_ref(&cwd),
&None,
/*use_legacy_landlock*/ false,
/*stdout_stream*/ None,
)
.await?;
assert_eq!(output.exit_code, 0, "sandboxed command should complete");
for name in codex_protocol::permissions::PROTECTED_METADATA_PATH_NAMES {
let path = cwd.join(name);
assert!(
!path.exists(),
"elevated setup should not create missing workspace metadata: {}",
path.display()
);
}
Ok(())
}
#[tokio::test]
#[serial(codex_home)]
async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyhow::Result<()> {
@@ -219,22 +276,26 @@ async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyh
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: exact_secret },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
+72 -1
View File
@@ -743,11 +743,13 @@ mod tests {
use codex_network_proxy::NetworkProxyConfig;
use codex_network_proxy::RemoteNetworkProxyConfig;
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_protocol::models::ManagedFileSystemPermissions;
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_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;
@@ -871,7 +873,75 @@ mod tests {
}
#[test]
fn filesystem_protocol_round_trips_permission_paths_as_uris() {
fn filesystem_protocol_round_trips_permission_entries() {
let native_cwd = std::env::current_dir().expect("current directory");
let cwd = PathUri::from_host_native_path(&native_cwd).expect("cwd URI");
let file_system = ManagedFileSystemPermissions::Restricted {
entries: vec![
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: native_cwd.clone().try_into().expect("absolute cwd"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Path {
path: native_cwd.join(".git").try_into().expect("absolute path"),
},
FileSystemAccessMode::Read,
),
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots {
subpath: Some(".codex".into()),
},
},
FileSystemAccessMode::Read,
),
],
glob_scan_max_depth: Some(2.try_into().expect("non-zero depth")),
};
let permissions = PermissionProfile::Managed {
file_system,
network: NetworkSandboxPolicy::Restricted,
};
let sandbox =
FileSystemSandboxContext::from_permission_profile_with_cwd(permissions, cwd.clone());
let serialized = serde_json::to_value(&sandbox).expect("serialize sandbox");
assert_eq!(
serialized["permissions"]["file_system"]["entries"][0]["path"]["path"],
serde_json::json!(cwd.to_string())
);
assert_eq!(
serialized["permissions"]["file_system"]["entries"][1]["path"]["type"],
serde_json::json!("path")
);
assert_eq!(
serialized["permissions"]["file_system"]["entries"][1]["missing_path_behavior"],
serde_json::json!("skip")
);
assert_eq!(
serialized["permissions"]["file_system"]["entries"][2]["path"]["type"],
serde_json::json!("special")
);
assert_eq!(
serialized["permissions"]["file_system"]["entries"][2]["missing_path_behavior"],
serde_json::json!("skip")
);
assert!(!serialized.to_string().contains("generated_default_path"));
assert!(!serialized.to_string().contains("generated_default_special"));
assert_eq!(
serde_json::from_value::<FileSystemSandboxContext>(serialized)
.expect("deserialize sandbox"),
sandbox
);
}
#[test]
fn filesystem_protocol_round_trips_legacy_policy_paths_as_uris() {
let native_cwd = std::env::current_dir().expect("current directory");
let cwd = PathUri::from_host_native_path(&native_cwd).expect("cwd URI");
let mut file_system_policy =
@@ -880,6 +950,7 @@ mod tests {
path: native_cwd.try_into().expect("absolute cwd"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
file_system_policy.glob_scan_max_depth = Some(2);
let permissions = PermissionProfile::from_runtime_permissions(
+11 -8
View File
@@ -206,12 +206,12 @@ fn add_helper_runtime_permissions(
cwd: &std::path::Path,
) {
if !file_system_policy.has_full_disk_read_access() {
let minimal_read_entry = FileSystemSandboxEntry {
path: FileSystemPath::Special {
let minimal_read_entry = FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
};
FileSystemAccessMode::Read,
);
if !file_system_policy.entries.contains(&minimal_read_entry) {
file_system_policy.entries.push(minimal_read_entry);
}
@@ -222,12 +222,12 @@ fn add_helper_runtime_permissions(
continue;
}
file_system_policy.entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Path {
file_system_policy.entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Path {
path: helper_read_root.clone(),
},
access: FileSystemAccessMode::Read,
});
FileSystemAccessMode::Read,
));
}
}
@@ -618,6 +618,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let sandbox_context = codex_file_system::FileSystemSandboxContext::from_permission_profile(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
@@ -710,6 +711,7 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access,
missing_path_behavior: None,
}
}
@@ -720,6 +722,7 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Special { value },
access,
missing_path_behavior: None,
}
}
}
@@ -406,6 +406,7 @@ mod tests {
path: absolute_test_path("remote-root"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let permissions =
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted);
@@ -427,6 +428,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let permissions =
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted);
@@ -52,6 +52,7 @@ async fn remote_file_system_sends_path_and_sandbox_cwd_uris_without_native_conve
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
@@ -205,12 +205,14 @@ async fn remote_process_keeps_sandbox_helper_visible_with_restricted_reads() ->
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
@@ -272,12 +274,14 @@ async fn remote_tty_process_uses_configured_sandbox_helper_with_hostile_path() -
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
@@ -332,6 +336,7 @@ async fn remote_process_preserves_empty_workspace_roots() -> Result<()> {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let mut sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
@@ -396,6 +396,7 @@ fn read_only_sandbox(path: std::path::PathBuf) -> FileSystemSandboxContext {
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
NetworkSandboxPolicy::Restricted,
))
@@ -86,6 +86,7 @@ pub(crate) fn read_only_sandbox(readable_root: std::path::PathBuf) -> FileSystem
path: readable_root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}])
}
@@ -98,6 +99,7 @@ pub(crate) fn workspace_write_sandbox(
path: writable_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}])
}
@@ -235,6 +235,7 @@ async fn remote_read_file_materializes_environment_workspace_roots() -> Result<(
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let mut sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
@@ -276,6 +277,7 @@ async fn remote_read_file_preserves_empty_workspace_roots() -> Result<()> {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let mut sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
@@ -143,12 +143,14 @@ fn summarizes_managed_workspace_write_permission_profile() {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: cwd.clone() },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: cache_root.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
+5
View File
@@ -8,6 +8,7 @@ use codex_protocol::models::SandboxEnforcement;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxEntryMissingPathBehavior;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::FileSystemSpecialPath;
@@ -160,6 +161,8 @@ impl TryFrom<ExecFileSystemPath> for FileSystemPath {
pub struct ExecFileSystemSandboxEntry {
pub path: ExecFileSystemPath,
pub access: FileSystemAccessMode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub missing_path_behavior: Option<FileSystemSandboxEntryMissingPathBehavior>,
}
impl From<FileSystemSandboxEntry> for ExecFileSystemSandboxEntry {
@@ -167,6 +170,7 @@ impl From<FileSystemSandboxEntry> for ExecFileSystemSandboxEntry {
Self {
path: value.path.into(),
access: value.access,
missing_path_behavior: value.missing_path_behavior,
}
}
}
@@ -178,6 +182,7 @@ impl TryFrom<ExecFileSystemSandboxEntry> for FileSystemSandboxEntry {
Ok(Self {
path: value.path.try_into()?,
access: value.access,
missing_path_behavior: value.missing_path_behavior,
})
}
}
+52 -8
View File
@@ -394,14 +394,14 @@ fn create_filesystem_args(
.iter()
.filter(|entry| entry.access == FileSystemAccessMode::Read)
.filter_map(|entry| {
let FileSystemPath::Special {
value:
FileSystemSpecialPath::ProjectRoots {
subpath: Some(subpath),
},
} = &entry.path
else {
return None;
let subpath = match &entry.path {
FileSystemPath::Special {
value:
FileSystemSpecialPath::ProjectRoots {
subpath: Some(subpath),
},
} => subpath,
_ => return None,
};
// Automatic repo-metadata read masks are skipped here so the
// metadata handling below can apply the root-scoped
@@ -1348,6 +1348,7 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern { pattern },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}
}
@@ -1426,6 +1427,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
unreadable_glob_entry(format!("{}/**/*.env", temp_dir.path().display())),
]);
@@ -1470,12 +1472,14 @@ mod tests {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -1544,10 +1548,12 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -1594,6 +1600,7 @@ mod tests {
path: logical_memories_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let args =
@@ -1633,6 +1640,7 @@ mod tests {
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path: root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let err =
@@ -1669,10 +1677,12 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_private },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -1704,10 +1714,12 @@ mod tests {
path: workspace_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: blocked_root },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -1750,6 +1762,7 @@ mod tests {
path: workspace_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let args =
@@ -1799,6 +1812,7 @@ mod tests {
path: workspace_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let args = create_filesystem_args(&policy, &workspace, NO_UNREADABLE_GLOB_SCAN_MAX_DEPTH)
@@ -1845,6 +1859,7 @@ mod tests {
path: link_workspace_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let args =
@@ -1914,30 +1929,35 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".git".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".agents".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".codex".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -1972,24 +1992,28 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".vscode".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".secrets".into())),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2105,6 +2129,7 @@ mod tests {
.expect("absolute readable root"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let args =
@@ -2132,6 +2157,7 @@ mod tests {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let args =
@@ -2169,10 +2195,12 @@ mod tests {
path: writable_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2242,16 +2270,19 @@ mod tests {
path: writable_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs.clone() },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: docs_public.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2294,18 +2325,21 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: blocked.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: allowed.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2363,18 +2397,21 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: blocked.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: allowed_file.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2444,14 +2481,17 @@ mod tests {
path: writable_root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: allowed },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2493,12 +2533,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: blocked.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2537,12 +2579,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: blocked_file.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -455,6 +455,7 @@ fn build_preflight_bwrap_argv(
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let preflight_command = vec![resolve_true_command()];
build_bwrap_argv(
@@ -221,10 +221,12 @@ fn split_only_filesystem_policy_requires_direct_runtime_enforcement() {
),
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs },
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -245,10 +247,12 @@ fn root_write_read_only_carveout_requires_direct_runtime_enforcement() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs },
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -566,10 +570,12 @@ fn resolve_permission_profile_preserves_direct_runtime_profile() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs },
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -620,10 +626,12 @@ fn legacy_landlock_rejects_split_only_filesystem_policies() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
missing_path_behavior: None,
},
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs },
access: codex_protocol::permissions::FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -787,6 +787,7 @@ async fn sandbox_blocks_explicit_split_policy_carveouts_under_bwrap() {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
@@ -794,18 +795,21 @@ async fn sandbox_blocks_explicit_split_policy_carveouts_under_bwrap() {
.expect("absolute helper dir"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::try_from(tmpdir.path()).expect("absolute tempdir"),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::try_from(blocked.as_path()).expect("absolute blocked dir"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -855,6 +859,7 @@ async fn sandbox_reenables_writable_subpaths_under_unreadable_parents() {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
@@ -862,24 +867,28 @@ async fn sandbox_reenables_writable_subpaths_under_unreadable_parents() {
.expect("absolute helper dir"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::try_from(tmpdir.path()).expect("absolute tempdir"),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::try_from(blocked.as_path()).expect("absolute blocked dir"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::try_from(allowed.as_path()).expect("absolute allowed dir"),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -926,12 +935,14 @@ async fn sandbox_blocks_root_read_carveouts_under_bwrap() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: AbsolutePathBuf::try_from(blocked.as_path()).expect("absolute blocked dir"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
@@ -194,6 +194,7 @@ fn builds_permissions_from_profile() {
path: writable_root.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]),
NetworkSandboxPolicy::Enabled,
);
@@ -230,18 +231,21 @@ fn builds_permissions_from_profile_with_denied_reads() {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_root.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: denied_glob.to_string_lossy().into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
+17 -10
View File
@@ -89,15 +89,19 @@ impl FileSystemPermissions {
) -> Self {
let mut entries = Vec::new();
if let Some(read) = read {
entries.extend(read.into_iter().map(|path| FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Read,
entries.extend(read.into_iter().map(|path| {
FileSystemSandboxEntry::new(
FileSystemPath::Path { path },
FileSystemAccessMode::Read,
)
}));
}
if let Some(write) = write {
entries.extend(write.into_iter().map(|path| FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Write,
entries.extend(write.into_iter().map(|path| {
FileSystemSandboxEntry::new(
FileSystemPath::Path { path },
FileSystemAccessMode::Write,
)
}));
}
Self {
@@ -636,12 +640,12 @@ impl From<&FileSystemSandboxPolicy> for FileSystemPermissions {
let entries = match value.kind {
FileSystemSandboxKind::Restricted => value.entries.clone(),
FileSystemSandboxKind::Unrestricted | FileSystemSandboxKind::ExternalSandbox => {
vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
vec![FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
}]
FileSystemAccessMode::Write,
)]
}
};
Self {
@@ -2497,6 +2501,7 @@ mod tests {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}]);
file_system_sandbox_policy.glob_scan_max_depth = Some(2);
@@ -2542,6 +2547,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: NonZeroUsize::new(2),
},
@@ -2685,6 +2691,7 @@ mod tests {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}],
glob_scan_max_depth: NonZeroUsize::new(2),
};
+208 -100
View File
@@ -174,6 +174,37 @@ impl FileSystemSpecialPath {
pub struct FileSystemSandboxEntry {
pub path: FileSystemPath,
pub access: FileSystemAccessMode,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub missing_path_behavior: Option<FileSystemSandboxEntryMissingPathBehavior>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
pub enum FileSystemSandboxEntryMissingPathBehavior {
Skip,
}
impl FileSystemSandboxEntry {
pub fn new(path: FileSystemPath, access: FileSystemAccessMode) -> Self {
Self {
path,
access,
missing_path_behavior: None,
}
}
pub fn skip_missing_path(path: FileSystemPath, access: FileSystemAccessMode) -> Self {
Self {
path,
access,
missing_path_behavior: Some(FileSystemSandboxEntryMissingPathBehavior::Skip),
}
}
pub fn skips_missing_path(&self) -> bool {
self.missing_path_behavior == Some(FileSystemSandboxEntryMissingPathBehavior::Skip)
}
}
#[derive(
@@ -355,12 +386,12 @@ pub fn project_roots_glob_pattern(subpath: &Path) -> String {
}
fn read_only_file_system_entries() -> Vec<FileSystemSandboxEntry> {
vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
vec![FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
}]
FileSystemAccessMode::Read,
)]
}
impl Default for FileSystemSandboxPolicy {
@@ -398,6 +429,26 @@ impl FileSystemSandboxPolicy {
}
}
/// Removes entries that should be skipped when their paths are missing.
///
/// Callers that materialize filesystem ACL targets should not turn these
/// entries into newly-created sentinel paths.
pub fn remove_skip_missing_path_entries(&mut self) {
self.entries.retain(|entry| !entry.skips_missing_path());
}
pub fn has_explicit_non_write_entry_for_path_with_cwd(&self, path: &Path, cwd: &Path) -> bool {
let Some(path) = resolve_candidate_path(path, cwd) else {
return false;
};
let cwd = AbsolutePathBuf::from_absolute_path(cwd).ok();
self.entries.iter().any(|entry| {
!entry.skips_missing_path()
&& !entry.access.can_write()
&& resolve_entry_path(&entry.path, cwd.as_ref()).as_ref() == Some(&path)
})
}
fn has_root_access(&self, predicate: impl Fn(FileSystemAccessMode) -> bool) -> bool {
matches!(self.kind, FileSystemSandboxKind::Restricted)
&& self.entries.iter().any(|entry| {
@@ -449,12 +500,12 @@ impl FileSystemSandboxPolicy {
.iter()
.any(|entry| entry.access == FileSystemAccessMode::Deny);
if matches!(self.kind, FileSystemSandboxKind::Unrestricted) && has_deny_read_entries {
*self = Self::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
*self = Self::restricted(vec![FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
}]);
FileSystemAccessMode::Write,
)]);
}
if !matches!(self.kind, FileSystemSandboxKind::Restricted) {
@@ -521,44 +572,38 @@ impl FileSystemSandboxPolicy {
exclude_tmpdir_env_var: bool,
exclude_slash_tmp: bool,
) -> Self {
let mut entries = vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
let mut entries = vec![FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
}];
FileSystemAccessMode::Read,
)];
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
});
FileSystemAccessMode::Write,
));
if !exclude_slash_tmp {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::SlashTmp,
},
access: FileSystemAccessMode::Write,
});
FileSystemAccessMode::Write,
));
}
if !exclude_tmpdir_env_var {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
});
FileSystemAccessMode::Write,
));
}
entries.extend(
writable_roots
.iter()
.cloned()
.map(|path| FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Write,
}),
);
entries.extend(writable_roots.iter().cloned().map(|path| {
FileSystemSandboxEntry::new(FileSystemPath::Path { path }, FileSystemAccessMode::Write)
}));
append_default_read_only_project_root_subpath_if_no_explicit_rule(&mut entries, ".git");
append_default_read_only_project_root_subpath_if_no_explicit_rule(&mut entries, ".agents");
@@ -758,6 +803,7 @@ impl FileSystemSandboxPolicy {
},
},
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
}));
}
FileSystemPath::GlobPattern { pattern } => {
@@ -767,11 +813,13 @@ impl FileSystemSandboxPolicy {
pattern: resolve_project_roots_glob_pattern(subpath, root),
},
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
}));
} else {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern { pattern },
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
});
}
}
@@ -779,12 +827,14 @@ impl FileSystemSandboxPolicy {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
});
}
FileSystemPath::Special { value } => {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special { value },
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
});
}
}
@@ -824,10 +874,10 @@ impl FileSystemSandboxPolicy {
continue;
}
self.entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Path { path: path.clone() },
access: FileSystemAccessMode::Read,
});
self.entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Path { path: path.clone() },
FileSystemAccessMode::Read,
));
}
self
@@ -843,10 +893,10 @@ impl FileSystemSandboxPolicy {
continue;
}
self.entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Path { path: path.clone() },
access: FileSystemAccessMode::Write,
});
self.entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Path { path: path.clone() },
FileSystemAccessMode::Write,
));
}
self
@@ -871,10 +921,10 @@ impl FileSystemSandboxPolicy {
entry.access.can_write()
&& matches!(&entry.path, FileSystemPath::Path { path: existing } if existing == path)
}) {
self.entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Path { path: path.clone() },
access: FileSystemAccessMode::Write,
});
self.entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Path { path: path.clone() },
FileSystemAccessMode::Write,
));
}
for protected_path in default_read_only_subpaths_for_writable_root(
@@ -1258,12 +1308,12 @@ impl From<&SandboxPolicy> for FileSystemSandboxPolicy {
SandboxPolicy::DangerFullAccess => FileSystemSandboxPolicy::unrestricted(),
SandboxPolicy::ExternalSandbox { .. } => FileSystemSandboxPolicy::external_sandbox(),
SandboxPolicy::ReadOnly { .. } => {
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
}])
FileSystemAccessMode::Read,
)])
}
SandboxPolicy::WorkspaceWrite {
writable_roots,
@@ -1600,45 +1650,39 @@ fn legacy_runtime_file_system_policy_for_cwd(
};
let mut entries = vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemAccessMode::Read,
),
FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
},
FileSystemAccessMode::Write,
),
];
if !*exclude_slash_tmp {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::SlashTmp,
},
access: FileSystemAccessMode::Write,
});
FileSystemAccessMode::Write,
));
}
if !*exclude_tmpdir_env_var {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special {
entries.push(FileSystemSandboxEntry::new(
FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
});
FileSystemAccessMode::Write,
));
}
entries.extend(
writable_roots
.iter()
.cloned()
.map(|path| FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Write,
}),
);
entries.extend(writable_roots.iter().cloned().map(|path| {
FileSystemSandboxEntry::new(FileSystemPath::Path { path }, FileSystemAccessMode::Write)
}));
if let Ok(cwd_root) = AbsolutePathBuf::from_absolute_path(cwd) {
for protected_path in default_read_only_subpaths_for_writable_root(
@@ -1689,10 +1733,10 @@ fn append_default_read_only_entry_if_no_explicit_rule(
return;
}
entries.push(FileSystemSandboxEntry {
entries.push(FileSystemSandboxEntry::skip_missing_path(
path,
access: FileSystemAccessMode::Read,
});
FileSystemAccessMode::Read,
));
}
fn has_explicit_resolved_path_entry(
@@ -1887,6 +1931,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
@@ -1896,6 +1941,7 @@ mod tests {
),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -1928,6 +1974,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let writable_roots = policy.get_writable_roots_with_cwd(cwd.path());
@@ -1957,31 +2004,33 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".git".into())),
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemAccessMode::Read,
),
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".agents".into())),
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemAccessMode::Read,
),
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".codex".into())),
},
access: FileSystemAccessMode::Read,
},
FileSystemAccessMode::Read,
),
])
);
}
@@ -2023,12 +2072,14 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: explicit_dot_codex.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2068,6 +2119,7 @@ mod tests {
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path: root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
assert!(!file_system_policy.can_write_path_with_cwd(&dot_git_config, cwd.path()));
@@ -2114,21 +2166,23 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
];
expected_entries.extend(PROTECTED_METADATA_PATH_NAMES.iter().map(|name| {
FileSystemSandboxEntry {
path: FileSystemPath::Special {
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some((*name).into())),
},
access: FileSystemAccessMode::Read,
}
FileSystemAccessMode::Read,
)
}));
expected_entries.extend(
default_read_only_subpaths_for_writable_root(
@@ -2136,9 +2190,11 @@ mod tests {
/*protect_missing_dot_codex*/ true,
)
.into_iter()
.map(|path| FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Read,
.map(|path| {
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Path { path },
FileSystemAccessMode::Read,
)
}),
);
@@ -2190,10 +2246,12 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2246,16 +2304,19 @@ mod tests {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2313,6 +2374,7 @@ mod tests {
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path: root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let writable_roots = policy.get_writable_roots_with_cwd(cwd.path());
@@ -2353,10 +2415,12 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_private },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2400,10 +2464,12 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_private },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2442,10 +2508,12 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: alias },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2502,10 +2570,12 @@ mod tests {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: link_blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2542,22 +2612,26 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs.clone() },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: docs_private.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: docs_private_public.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2589,10 +2663,12 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -2675,10 +2751,12 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs.clone() },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -2713,10 +2791,12 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs.clone() },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
]);
@@ -2743,12 +2823,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2769,14 +2851,17 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs.clone() },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs.clone() },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -2796,6 +2881,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
let actual = policy
@@ -2814,6 +2900,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let actual = policy
@@ -2834,6 +2921,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let actual = policy.with_additional_writable_roots(&cwd, std::slice::from_ref(&extra));
@@ -2846,10 +2934,12 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: extra },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
])
);
@@ -2868,18 +2958,21 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".git".into())),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: project_roots_glob_pattern(Path::new("**/*.env")),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -2894,24 +2987,28 @@ mod tests {
path: first.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: second.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: first.join(".git"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: second.join(".git"),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
@@ -2923,6 +3020,7 @@ mod tests {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
@@ -2934,6 +3032,7 @@ mod tests {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
])
);
@@ -2947,6 +3046,7 @@ mod tests {
pattern: project_roots_glob_pattern(Path::new("**/*.env")),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}]);
let actual = policy.materialize_project_roots_with_cwd(cwd.path());
@@ -2960,6 +3060,7 @@ mod tests {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}])
);
}
@@ -2975,6 +3076,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let actual =
@@ -2988,19 +3090,21 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: extra.clone()
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
FileSystemSandboxEntry::skip_missing_path(
FileSystemPath::Path {
path: extra.join(".git")
},
access: FileSystemAccessMode::Read,
},
FileSystemAccessMode::Read,
),
])
);
}
@@ -3019,6 +3123,7 @@ mod tests {
path: denied.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}]);
let rebuilt = FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries(
@@ -3054,6 +3159,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
deny_entry,
]);
@@ -3067,6 +3173,7 @@ mod tests {
path: AbsolutePathBuf::try_from(path).expect("absolute deny path"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}])
}
@@ -3074,6 +3181,7 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern { pattern },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}
}
+12
View File
@@ -4930,6 +4930,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
assert!(read_only.has_full_disk_read_access());
assert!(!read_only.has_full_disk_write_access());
@@ -4940,6 +4941,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
assert!(writable.has_full_disk_read_access());
assert!(writable.has_full_disk_write_access());
@@ -4970,10 +4972,12 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -5021,16 +5025,19 @@ mod tests {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: secret },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -5089,14 +5096,17 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: docs_public },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
]);
@@ -5133,6 +5143,7 @@ mod tests {
path: external_write_path,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
let err = policy
@@ -6115,6 +6126,7 @@ mod tests {
pattern: "/tmp/private/**/*.txt".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
])),
model: "gpt-5".to_string(),
+14
View File
@@ -64,6 +64,7 @@ fn restricted_file_system_uses_platform_sandbox_without_managed_network() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
NetworkSandboxPolicy::Enabled,
SandboxablePreference::Auto,
@@ -194,12 +195,14 @@ fn transform_additional_permissions_preserves_denied_entries() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_path.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let permissions = PermissionProfile::from_runtime_permissions(
@@ -243,14 +246,17 @@ fn transform_additional_permissions_preserves_denied_entries() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: denied_path },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: allowed_path },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
])
);
@@ -274,6 +280,7 @@ fn managed_mitm_ca_bundle_becomes_readable_for_restricted_sandbox() {
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path: cwd.clone() },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),
NetworkSandboxPolicy::Restricted,
);
@@ -291,12 +298,14 @@ fn managed_mitm_ca_bundle_becomes_readable_for_restricted_sandbox() {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: cwd },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: managed_bundle_path,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
])
);
@@ -342,6 +351,7 @@ fn wsl1_rejects_linux_bubblewrap_path() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
assert!(matches!(
@@ -391,6 +401,7 @@ fn wsl1_allows_non_bubblewrap_linux_paths() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]);
assert!(
super::ensure_linux_bubblewrap_is_supported(
@@ -476,16 +487,19 @@ fn transform_for_direct_spawn_windows_materializes_inner_helper() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
NetworkSandboxPolicy::Restricted,
@@ -49,6 +49,7 @@ pub fn normalize_additional_permissions(
let normalized_entry = FileSystemSandboxEntry {
path,
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
};
if !entries.contains(&normalized_entry) {
entries.push(normalized_entry);
@@ -355,6 +356,7 @@ fn materialize_cwd_dependent_entry(
.map(|path| FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
})
.unwrap_or_else(|| entry.clone()),
FileSystemPath::GlobPattern { pattern } => FileSystemSandboxEntry {
@@ -364,6 +366,7 @@ fn materialize_cwd_dependent_entry(
.into_owned(),
},
access: entry.access,
missing_path_behavior: entry.missing_path_behavior,
},
FileSystemPath::Path { .. } | FileSystemPath::Special { .. } => entry.clone(),
}
@@ -31,6 +31,7 @@ fn full_access_restricted_policy_skips_platform_sandbox_when_network_is_enabled(
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
assert_eq!(
@@ -55,10 +56,12 @@ fn root_write_policy_with_carveouts_still_uses_platform_sandbox() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: blocked },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -79,6 +82,7 @@ fn full_access_restricted_policy_still_uses_platform_sandbox_for_restricted_netw
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]);
assert_eq!(
@@ -166,6 +170,7 @@ fn normalize_additional_permissions_rejects_glob_read_grants() {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -188,6 +193,7 @@ fn normalize_additional_permissions_preserves_deny_globs() {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
}),
@@ -204,6 +210,7 @@ fn normalize_additional_permissions_preserves_deny_globs() {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
}),
@@ -302,6 +309,7 @@ fn intersect_permission_profiles_accepts_child_path_granted_for_requested_cwd()
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -335,6 +343,7 @@ fn intersect_permission_profiles_materializes_cwd_grant_for_reuse() {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -386,10 +395,12 @@ fn intersect_permission_profiles_deduplicates_materialized_grants() {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: cwd.clone() },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -422,12 +433,14 @@ fn intersect_permission_profiles_materializes_cwd_deny_entries() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -445,10 +458,12 @@ fn intersect_permission_profiles_materializes_cwd_deny_entries() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: request_cwd },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -477,10 +492,12 @@ fn intersect_permission_profiles_drops_deny_entries_without_filesystem_grants()
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: secret },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -515,12 +532,14 @@ fn intersect_permission_profiles_rejects_concrete_grants_matched_by_requested_de
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
@@ -553,12 +572,14 @@ fn intersect_permission_profiles_materializes_relative_deny_globs_for_reuse() {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
};
let deny_env_files = FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
};
let permissions = PermissionProfile {
file_system: Some(FileSystemPermissions {
@@ -581,12 +602,14 @@ fn intersect_permission_profiles_materializes_relative_deny_globs_for_reuse() {
path: request_cwd.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: request_cwd.join("**/*.env").to_string_lossy().into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
@@ -632,6 +655,7 @@ fn intersect_permission_profiles_drops_broader_cwd_grant_for_requested_child_pat
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
}),
@@ -652,12 +676,14 @@ fn intersect_permission_profiles_uses_granted_bounded_glob_scan_depth() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
};
let deny_env_files = FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
};
let requested = PermissionProfile {
file_system: Some(FileSystemPermissions {
@@ -690,6 +716,7 @@ fn intersect_permission_profiles_uses_granted_bounded_glob_scan_depth() {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: std::num::NonZeroUsize::new(4),
@@ -707,12 +734,14 @@ fn intersect_permission_profiles_uses_granted_unbounded_glob_scan_depth() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
};
let deny_env_files = FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
};
let requested = PermissionProfile {
file_system: Some(FileSystemPermissions {
@@ -745,6 +774,7 @@ fn intersect_permission_profiles_uses_granted_unbounded_glob_scan_depth() {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -770,12 +800,14 @@ fn merge_file_system_policy_with_additional_permissions_preserves_unreadable_roo
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_path.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]),
&FileSystemPermissions::from_read_write_roots(
@@ -788,6 +820,7 @@ fn merge_file_system_policy_with_additional_permissions_preserves_unreadable_roo
merged_policy.entries.contains(&FileSystemSandboxEntry {
path: FileSystemPath::Path { path: denied_path },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}),
true
);
@@ -795,6 +828,7 @@ fn merge_file_system_policy_with_additional_permissions_preserves_unreadable_roo
merged_policy.entries.contains(&FileSystemSandboxEntry {
path: FileSystemPath::Path { path: allowed_path },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}),
true
);
@@ -807,6 +841,7 @@ fn merge_file_system_policy_with_additional_permissions_carries_bounded_glob_sca
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
};
let merged_policy = merge_file_system_policy_with_additional_permissions(
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
@@ -814,6 +849,7 @@ fn merge_file_system_policy_with_additional_permissions_carries_bounded_glob_sca
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}]),
&FileSystemPermissions {
entries: vec![deny_env_files.clone()],
@@ -828,6 +864,7 @@ fn merge_file_system_policy_with_additional_permissions_carries_bounded_glob_sca
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
deny_env_files,
]);
@@ -850,10 +887,12 @@ fn effective_file_system_sandbox_policy_returns_base_policy_without_additional_p
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: denied_path },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -878,12 +917,14 @@ fn effective_file_system_sandbox_policy_merges_additional_write_roots() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: denied_path.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
let additional_permissions = PermissionProfile {
@@ -901,6 +942,7 @@ fn effective_file_system_sandbox_policy_merges_additional_write_roots() {
effective_policy.entries.contains(&FileSystemSandboxEntry {
path: FileSystemPath::Path { path: denied_path },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}),
true
);
@@ -908,6 +950,7 @@ fn effective_file_system_sandbox_policy_merges_additional_write_roots() {
effective_policy.entries.contains(&FileSystemSandboxEntry {
path: FileSystemPath::Path { path: allowed_path },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}),
true
);
@@ -193,10 +193,12 @@ fn explicit_unreadable_paths_are_excluded_from_full_disk_read_and_write_access()
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: unreadable },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -299,10 +301,12 @@ fn explicit_unreadable_paths_are_excluded_from_readable_roots() {
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: root },
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: unreadable },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
]);
@@ -411,6 +415,7 @@ fn unreadable_glob_policy_includes_canonicalized_static_prefix() {
policy.entries.push(FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern { pattern },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
});
let seatbelt_policy = build_seatbelt_unreadable_glob_policy(&policy, temp_dir.path());
+27 -4
View File
@@ -87,7 +87,7 @@ pub fn resolve_windows_restricted_token_filesystem_overrides(
return Ok(None);
}
let (file_system_sandbox_policy, network_sandbox_policy) =
let (mut file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let needs_direct_runtime_enforcement = file_system_sandbox_policy
@@ -107,6 +107,11 @@ pub fn resolve_windows_restricted_token_filesystem_overrides(
));
}
// Windows protects existing metadata paths through the legacy writable root
// projection. Do not turn skip-missing entries into newly-created
// deny-write sentinels.
file_system_sandbox_policy.remove_skip_missing_path_entries();
// The restricted-token backend can still enforce split write restrictions,
// but its WRITE_RESTRICTED token does not make capability SID deny-read ACEs
// participate in read access checks. Read restrictions therefore require the
@@ -221,7 +226,7 @@ pub fn resolve_windows_elevated_filesystem_overrides(
return Ok(None);
}
let (file_system_sandbox_policy, network_sandbox_policy) =
let (mut file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
if !permission_profile_supports_windows_restricted_token_sandbox(permission_profile) {
@@ -232,6 +237,11 @@ pub fn resolve_windows_elevated_filesystem_overrides(
));
}
// Windows protects existing metadata paths through the legacy writable root
// projection. Do not turn skip-missing entries into newly-created
// deny-write sentinels.
file_system_sandbox_policy.remove_skip_missing_path_entries();
let additional_deny_read_paths = codex_windows_sandbox::resolve_windows_deny_read_paths(
&file_system_sandbox_policy,
sandbox_policy_cwd,
@@ -248,6 +258,12 @@ pub fn resolve_windows_elevated_filesystem_overrides(
let needs_direct_runtime_enforcement = file_system_sandbox_policy
.needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd);
let has_explicit_write_carveouts = split_writable_roots.iter().any(|writable_root| {
writable_root.read_only_subpaths.iter().any(|path| {
file_system_sandbox_policy
.has_explicit_non_write_entry_for_path_with_cwd(path.as_path(), sandbox_policy_cwd)
})
});
let normalize_path = |path: PathBuf| dunce::canonicalize(&path).unwrap_or(path);
let legacy_projection = compatibility_sandbox_policy_for_permission_profile(
permission_profile,
@@ -288,7 +304,9 @@ pub fn resolve_windows_elevated_filesystem_overrides(
Some(split_root_paths)
};
let additional_deny_write_paths = if needs_direct_runtime_enforcement {
let additional_deny_write_paths = if needs_direct_runtime_enforcement
|| has_explicit_write_carveouts
{
let mut deny_paths = BTreeSet::new();
for writable_root in &split_writable_roots {
let writable_root_path = normalize_path(writable_root.root.to_path_buf());
@@ -309,7 +327,12 @@ pub fn resolve_windows_elevated_filesystem_overrides(
== read_only_subpath_suffix
})
});
if !already_denied_by_legacy {
let explicitly_configured = file_system_sandbox_policy
.has_explicit_non_write_entry_for_path_with_cwd(
read_only_subpath.as_path(),
sandbox_policy_cwd,
);
if explicitly_configured || !already_denied_by_legacy {
deny_paths.insert(normalize_path(read_only_subpath.to_path_buf()));
}
}
+2
View File
@@ -112,12 +112,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: "/tmp/writable".try_into().expect("absolute path"),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -300,12 +300,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/.env".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
+4
View File
@@ -2230,10 +2230,12 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: extra_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -2258,12 +2260,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { subpath: None },
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -253,12 +253,14 @@ async fn submission_includes_configured_active_permission_profile() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "/home/user/project/secrets/**".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -481,12 +481,14 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/.secret".to_string(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -20,28 +20,33 @@ fn app_server_workspace_write_profile(extra_root: AbsolutePathBuf) -> Permission
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { subpath: None },
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::SlashTmp,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path { path: extra_root },
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -359,12 +364,14 @@ async fn preset_matching_does_not_treat_non_cwd_writable_profile_as_read_only()
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: test_path_buf("/tmp/writable").abs(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
+2
View File
@@ -65,12 +65,14 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: extra_root.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
+4
View File
@@ -101,24 +101,28 @@ fn app_server_workspace_write_profile(network_enabled: bool) -> PermissionProfil
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { subpath: None },
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::SlashTmp,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Tmpdir,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -44,6 +44,7 @@ pub fn resolve_windows_deny_read_paths(
pattern: pattern.clone(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
})
.collect(),
);
@@ -206,6 +207,7 @@ mod tests {
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern { pattern },
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}
}
@@ -215,6 +217,7 @@ mod tests {
path: AbsolutePathBuf::from_absolute_path(path).expect("absolute path"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
}
}
@@ -173,7 +173,7 @@ impl ResolvedWindowsSandboxPermissions {
self.file_system
.entries
.iter()
.any(|FileSystemSandboxEntry { path, access }| {
.any(|FileSystemSandboxEntry { path, access, .. }| {
matches!(
path,
FileSystemPath::Special {
@@ -258,6 +258,7 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
},
@@ -298,18 +299,21 @@ mod tests {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(".git".into())),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: project_roots_glob_pattern(Path::new("**/*.env")),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
],
glob_scan_max_depth: None,
@@ -332,24 +336,28 @@ mod tests {
path: first.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: second.clone(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: first.join(".git"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: second.join(".git"),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
@@ -361,6 +369,7 @@ mod tests {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
@@ -372,6 +381,7 @@ mod tests {
.into_owned(),
},
access: FileSystemAccessMode::Deny,
missing_path_behavior: None,
},
])
);
@@ -455,6 +465,7 @@ mod tests {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,
}],
glob_scan_max_depth: None,
},