protocol: preserve glob scan depth in permission profiles (#18713)

## Why

#18274 made `PermissionProfile` the canonical file-system permissions
shape, but the round-trip from `FileSystemSandboxPolicy` to
`PermissionProfile` still dropped one piece of policy metadata:
`glob_scan_max_depth`.

That field is security-relevant for deny-read globs such as `**/*.env`.
On Linux, bubblewrap sandbox construction uses it to bound unreadable
glob expansion. If a profile copied from active runtime permissions
loses this value and is submitted back as an override, the resulting
`FileSystemSandboxPolicy` can behave differently even though the visible
permission entries look equivalent.

## What changed

- Add `glob_scan_max_depth` to protocol `FileSystemPermissions` and
preserve it when converting to/from `FileSystemSandboxPolicy`.
- Keep legacy `read`/`write` JSON for simple path-only permissions, but
force canonical JSON when glob scan depth is present so the metadata is
not silently dropped.
- Carry `globScanMaxDepth` through app-server
`AdditionalFileSystemPermissions`, generated JSON/TypeScript schemas,
and app-server/TUI conversion call sites.
- Preserve the metadata through sandboxing permission normalization,
merging, and intersection.
- Carry the merged scan depth into the effective
`FileSystemSandboxPolicy` used for command execution, so bounded
deny-read globs reach Linux bubblewrap materialization.

## Verification

- `cargo test -p codex-sandboxing glob_scan -- --nocapture`
- `cargo test -p codex-sandboxing policy_transforms -- --nocapture`
- `just fix -p codex-sandboxing`





---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18713).
* #18288
* #18287
* #18286
* #18285
* #18284
* #18283
* #18282
* #18281
* #18280
* #18279
* #18278
* #18277
* #18276
* #18275
* __->__ #18713
This commit is contained in:
Michael Bolin
2026-04-20 19:42:45 -07:00
committed by GitHub
Unverified
parent 6e9e2c2eef
commit 3d2f123895
18 changed files with 352 additions and 12 deletions
@@ -16,6 +16,14 @@
"null"
]
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"read": {
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
@@ -16,6 +16,14 @@
"null"
]
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"read": {
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
@@ -16,6 +16,14 @@
"null"
]
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"read": {
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
@@ -16,6 +16,14 @@
"null"
]
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"read": {
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
@@ -16,6 +16,14 @@
"null"
]
},
"globScanMaxDepth": {
"format": "uint",
"minimum": 1.0,
"type": [
"integer",
"null"
]
},
"read": {
"items": {
"$ref": "#/definitions/v2/AbsolutePathBuf"
@@ -4,4 +4,4 @@
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { FileSystemSandboxEntry } from "./FileSystemSandboxEntry";
export type AdditionalFileSystemPermissions = { read: Array<AbsolutePathBuf> | null, write: Array<AbsolutePathBuf> | null, entries?: Array<FileSystemSandboxEntry>, };
export type AdditionalFileSystemPermissions = { read: Array<AbsolutePathBuf> | null, write: Array<AbsolutePathBuf> | null, globScanMaxDepth?: number, entries?: Array<FileSystemSandboxEntry>, };
@@ -2056,6 +2056,7 @@ mod tests {
file_system: Some(v2::AdditionalFileSystemPermissions {
read: Some(vec![absolute_path("/tmp/allowed")]),
write: None,
glob_scan_max_depth: None,
entries: None,
}),
}),
@@ -1,5 +1,6 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use crate::RequestId;
@@ -1162,6 +1163,9 @@ pub struct AdditionalFileSystemPermissions {
pub write: Option<Vec<AbsolutePathBuf>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub glob_scan_max_depth: Option<NonZeroUsize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub entries: Option<Vec<FileSystemSandboxEntry>>,
}
@@ -1171,12 +1175,14 @@ impl From<CoreFileSystemPermissions> for AdditionalFileSystemPermissions {
Self {
read,
write,
glob_scan_max_depth: None,
entries: None,
}
} else {
Self {
read: None,
write: None,
glob_scan_max_depth: value.glob_scan_max_depth,
entries: Some(
value
.entries
@@ -1191,16 +1197,19 @@ impl From<CoreFileSystemPermissions> for AdditionalFileSystemPermissions {
impl From<AdditionalFileSystemPermissions> for CoreFileSystemPermissions {
fn from(value: AdditionalFileSystemPermissions) -> Self {
if let Some(entries) = value.entries {
let mut permissions = if let Some(entries) = value.entries {
Self {
entries: entries
.into_iter()
.map(CoreFileSystemSandboxEntry::from)
.collect(),
glob_scan_max_depth: None,
}
} else {
CoreFileSystemPermissions::from_read_write_roots(value.read, value.write)
}
};
permissions.glob_scan_max_depth = value.glob_scan_max_depth;
permissions
}
}
@@ -6950,6 +6959,7 @@ mod tests {
use codex_utils_absolute_path::test_support::test_path_buf;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::num::NonZeroUsize;
use std::path::PathBuf;
fn absolute_path_string(path: &str) -> String {
@@ -7084,6 +7094,7 @@ mod tests {
AbsolutePathBuf::try_from(PathBuf::from(read_write_path))
.expect("path must be absolute"),
]),
glob_scan_max_depth: None,
entries: None,
}),
}
@@ -7155,6 +7166,7 @@ mod tests {
access: CoreFileSystemAccessMode::None,
},
],
glob_scan_max_depth: NonZeroUsize::new(2),
};
let permissions = AdditionalFileSystemPermissions::from(core_permissions.clone());
@@ -7163,6 +7175,7 @@ mod tests {
AdditionalFileSystemPermissions {
read: None,
write: None,
glob_scan_max_depth: NonZeroUsize::new(2),
entries: Some(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
@@ -7185,6 +7198,17 @@ mod tests {
);
}
#[test]
fn additional_file_system_permissions_rejects_zero_glob_scan_depth() {
serde_json::from_value::<AdditionalFileSystemPermissions>(json!({
"read": null,
"write": null,
"globScanMaxDepth": 0,
"entries": [],
}))
.expect_err("zero glob scan depth should fail deserialization");
}
#[test]
fn permissions_request_approval_response_uses_granted_permission_profile_without_macos() {
let read_only_path = if cfg!(windows) {
@@ -7225,6 +7249,7 @@ mod tests {
AbsolutePathBuf::try_from(PathBuf::from(read_write_path))
.expect("path must be absolute"),
]),
glob_scan_max_depth: None,
entries: None,
}),
}
+2
View File
@@ -781,6 +781,7 @@ mod tests {
codex_app_server_protocol::AdditionalFileSystemPermissions {
read: Some(vec![absolute_path("/tmp/allowed")]),
write: None,
glob_scan_max_depth: None,
entries: None,
},
),
@@ -844,6 +845,7 @@ mod tests {
codex_app_server_protocol::AdditionalFileSystemPermissions {
read: Some(vec![absolute_path("/tmp/allowed")]),
write: None,
glob_scan_max_depth: None,
entries: None,
},
),
@@ -93,6 +93,7 @@ async fn request_permissions_round_trip() -> Result<()> {
file_system: Some(codex_app_server_protocol::AdditionalFileSystemPermissions {
read: None,
write: Some(vec![requested_writes[0].clone()]),
glob_scan_max_depth: None,
entries: None,
}),
},
+90 -6
View File
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::num::NonZeroUsize;
use std::path::Path;
use std::path::PathBuf;
use std::sync::LazyLock;
@@ -146,6 +147,7 @@ impl SandboxPermissions {
#[derive(Debug, Clone, Default, Eq, Hash, PartialEq, JsonSchema, TS)]
pub struct FileSystemPermissions {
pub entries: Vec<FileSystemSandboxEntry>,
pub glob_scan_max_depth: Option<NonZeroUsize>,
}
pub type LegacyReadWriteRoots = (Option<Vec<AbsolutePathBuf>>, Option<Vec<AbsolutePathBuf>>);
@@ -172,7 +174,10 @@ impl FileSystemPermissions {
access: FileSystemAccessMode::Write,
}));
}
Self { entries }
Self {
entries,
glob_scan_max_depth: None,
}
}
pub fn explicit_path_entries(
@@ -190,6 +195,10 @@ impl FileSystemPermissions {
}
fn as_legacy_permissions(&self) -> Option<LegacyFileSystemPermissions> {
if self.glob_scan_max_depth.is_some() {
return None;
}
let mut read = Vec::new();
let mut write = Vec::new();
@@ -225,6 +234,8 @@ struct LegacyFileSystemPermissions {
struct CanonicalFileSystemPermissions {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
entries: Vec<FileSystemSandboxEntry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
glob_scan_max_depth: Option<NonZeroUsize>,
}
#[derive(Debug, Clone, Deserialize)]
@@ -244,6 +255,7 @@ impl Serialize for FileSystemPermissions {
} else {
CanonicalFileSystemPermissions {
entries: self.entries.clone(),
glob_scan_max_depth: self.glob_scan_max_depth,
}
.serialize(serializer)
}
@@ -256,9 +268,13 @@ impl<'de> Deserialize<'de> for FileSystemPermissions {
D: Deserializer<'de>,
{
match FileSystemPermissionsDe::deserialize(deserializer)? {
FileSystemPermissionsDe::Canonical(CanonicalFileSystemPermissions { entries }) => {
Ok(Self { entries })
}
FileSystemPermissionsDe::Canonical(CanonicalFileSystemPermissions {
entries,
glob_scan_max_depth,
}) => Ok(Self {
entries,
glob_scan_max_depth,
}),
FileSystemPermissionsDe::Legacy(LegacyFileSystemPermissions { read, write }) => {
Ok(Self::from_read_write_roots(read, write))
}
@@ -352,13 +368,18 @@ impl From<&FileSystemSandboxPolicy> for FileSystemPermissions {
}]
}
};
Self { entries }
Self {
entries,
glob_scan_max_depth: value.glob_scan_max_depth.and_then(NonZeroUsize::new),
}
}
}
impl From<&FileSystemPermissions> for FileSystemSandboxPolicy {
fn from(value: &FileSystemPermissions) -> Self {
FileSystemSandboxPolicy::restricted(value.entries.clone())
let mut policy = FileSystemSandboxPolicy::restricted(value.entries.clone());
policy.glob_scan_max_depth = value.glob_scan_max_depth.map(usize::from);
policy
}
}
@@ -1828,6 +1849,69 @@ mod tests {
assert_eq!(permission_profile.is_empty(), false);
}
#[test]
fn permission_profile_round_trip_preserves_glob_scan_max_depth() {
let mut file_system_sandbox_policy =
FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::None,
}]);
file_system_sandbox_policy.glob_scan_max_depth = Some(2);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_sandbox_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
permission_profile.file_system_sandbox_policy(),
file_system_sandbox_policy
);
}
#[test]
fn file_system_permissions_with_glob_scan_depth_uses_canonical_json() -> Result<()> {
let path = AbsolutePathBuf::try_from(PathBuf::from(if cfg!(windows) {
r"C:\tmp\allowed"
} else {
"/tmp/allowed"
}))
.expect("absolute path");
let file_system_permissions = FileSystemPermissions {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
access: FileSystemAccessMode::Read,
}],
glob_scan_max_depth: NonZeroUsize::new(2),
};
let serialized = serde_json::to_value(&file_system_permissions)?;
assert_eq!(serialized.get("read"), None);
assert_eq!(serialized.get("write"), None);
assert_eq!(
serialized.get("glob_scan_max_depth"),
Some(&serde_json::json!(2))
);
assert!(serialized.get("entries").is_some());
assert_eq!(
serde_json::from_value::<FileSystemPermissions>(serialized)?,
file_system_permissions
);
Ok(())
}
#[test]
fn file_system_permissions_rejects_zero_glob_scan_depth() {
serde_json::from_value::<FileSystemPermissions>(serde_json::json!({
"entries": [],
"glob_scan_max_depth": 0,
}))
.expect_err("zero glob scan depth should fail deserialization");
}
#[test]
fn convert_mcp_content_to_items_builds_data_urls_when_missing_prefix() {
let contents = vec![serde_json::json!({
+72 -3
View File
@@ -13,6 +13,7 @@ use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_absolute_path::canonicalize_preserving_symlinks;
use std::collections::HashSet;
use std::num::NonZeroUsize;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EffectiveSandboxPermissions {
@@ -45,6 +46,7 @@ pub fn normalize_additional_permissions(
let file_system = match additional_permissions.file_system {
Some(file_system) => {
let mut entries = Vec::with_capacity(file_system.entries.len());
let glob_scan_max_depth = file_system.glob_scan_max_depth;
for entry in file_system.entries {
if matches!(&entry.path, FileSystemPath::GlobPattern { .. })
&& entry.access != FileSystemAccessMode::None
@@ -73,7 +75,10 @@ pub fn normalize_additional_permissions(
entries.push(normalized_entry);
}
}
let file_system = FileSystemPermissions { entries };
let file_system = FileSystemPermissions {
entries,
glob_scan_max_depth,
};
(!file_system.is_empty()).then_some(file_system)
}
None => None,
@@ -114,6 +119,13 @@ pub fn merge_permission_profiles(
let file_system = match (base.file_system.as_ref(), permissions.file_system.as_ref()) {
(Some(base), Some(permissions)) => Some(FileSystemPermissions {
entries: merge_permission_entries(&base.entries, &permissions.entries),
glob_scan_max_depth: merge_glob_scan_max_depth(
&base.entries,
base.glob_scan_max_depth.map(usize::from),
&permissions.entries,
permissions.glob_scan_max_depth.map(usize::from),
)
.and_then(NonZeroUsize::new),
})
.filter(|file_system| !file_system.is_empty()),
(Some(base), None) => Some(base.clone()),
@@ -139,12 +151,21 @@ pub fn intersect_permission_profiles(
.file_system
.map(|requested_file_system| {
let granted_file_system = granted.file_system.unwrap_or_default();
let entries = requested_file_system
let entries: Vec<_> = requested_file_system
.entries
.into_iter()
.filter(|entry| granted_file_system.entries.contains(entry))
.collect();
FileSystemPermissions { entries }
FileSystemPermissions {
glob_scan_max_depth: merge_glob_scan_max_depth(
&entries,
requested_file_system.glob_scan_max_depth.map(usize::from),
&entries,
granted_file_system.glob_scan_max_depth.map(usize::from),
)
.and_then(NonZeroUsize::new),
entries,
}
})
.filter(|file_system| !file_system.is_empty());
let network = match (requested.network, granted.network) {
@@ -167,6 +188,48 @@ pub fn intersect_permission_profiles(
}
}
fn merge_glob_scan_max_depth(
left_entries: &[FileSystemSandboxEntry],
left_depth: Option<usize>,
right_entries: &[FileSystemSandboxEntry],
right_depth: Option<usize>,
) -> Option<usize> {
let left_depth = effective_glob_scan_depth(left_entries, left_depth);
let right_depth = effective_glob_scan_depth(right_entries, right_depth);
match (left_depth, right_depth) {
(Some(GlobScanDepth::Unbounded), _) | (_, Some(GlobScanDepth::Unbounded)) => None,
(Some(GlobScanDepth::Bounded(left)), Some(GlobScanDepth::Bounded(right))) => {
Some(left.max(right))
}
(Some(GlobScanDepth::Bounded(depth)), None)
| (None, Some(GlobScanDepth::Bounded(depth))) => Some(depth),
(None, None) => None,
}
}
fn effective_glob_scan_depth(
entries: &[FileSystemSandboxEntry],
depth: Option<usize>,
) -> Option<GlobScanDepth> {
entries
.iter()
.any(|entry| {
entry.access == FileSystemAccessMode::None
&& matches!(&entry.path, FileSystemPath::GlobPattern { .. })
})
.then_some(match depth {
Some(depth) => GlobScanDepth::Bounded(depth),
None => GlobScanDepth::Unbounded,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum GlobScanDepth {
Bounded(usize),
Unbounded,
}
fn merge_permission_entries(
base: &[FileSystemSandboxEntry],
permissions: &[FileSystemSandboxEntry],
@@ -234,6 +297,12 @@ fn merge_file_system_policy_with_additional_permissions(
merged_policy.entries.push(entry.clone());
}
}
merged_policy.glob_scan_max_depth = merge_glob_scan_max_depth(
&file_system_policy.entries,
file_system_policy.glob_scan_max_depth,
&additional_permissions.entries,
additional_permissions.glob_scan_max_depth.map(usize::from),
);
merged_policy
}
FileSystemSandboxKind::Unrestricted | FileSystemSandboxKind::ExternalSandbox => {
@@ -171,6 +171,7 @@ fn normalize_additional_permissions_rejects_glob_read_grants() {
},
access: FileSystemAccessMode::Read,
}],
glob_scan_max_depth: None,
}),
..Default::default()
})
@@ -192,6 +193,7 @@ fn normalize_additional_permissions_preserves_deny_globs() {
},
access: FileSystemAccessMode::None,
}],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
}),
..Default::default()
})
@@ -207,6 +209,7 @@ fn normalize_additional_permissions_preserves_deny_globs() {
},
access: FileSystemAccessMode::None,
}],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
}),
..Default::default()
}
@@ -288,6 +291,76 @@ fn intersect_permission_profiles_drops_explicit_empty_reads_without_grant() {
);
}
#[test]
fn intersect_permission_profiles_uses_granted_bounded_glob_scan_depth() {
let deny_env_files = FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::None,
};
let requested = PermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![deny_env_files.clone()],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
}),
..Default::default()
};
let granted = PermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![deny_env_files.clone()],
glob_scan_max_depth: std::num::NonZeroUsize::new(4),
}),
..Default::default()
};
assert_eq!(
intersect_permission_profiles(requested, granted),
PermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![deny_env_files],
glob_scan_max_depth: std::num::NonZeroUsize::new(4),
}),
..Default::default()
}
);
}
#[test]
fn intersect_permission_profiles_uses_granted_unbounded_glob_scan_depth() {
let deny_env_files = FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::None,
};
let requested = PermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![deny_env_files.clone()],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
}),
..Default::default()
};
let granted = PermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![deny_env_files.clone()],
glob_scan_max_depth: None,
}),
..Default::default()
};
assert_eq!(
intersect_permission_profiles(requested, granted),
PermissionProfile {
file_system: Some(FileSystemPermissions {
entries: vec![deny_env_files],
glob_scan_max_depth: None,
}),
..Default::default()
}
);
}
#[test]
fn read_only_additional_permissions_can_enable_network_without_writes() {
let temp_dir = TempDir::new().expect("create temp dir");
@@ -402,6 +475,42 @@ fn merge_file_system_policy_with_additional_permissions_preserves_unreadable_roo
);
}
#[test]
fn merge_file_system_policy_with_additional_permissions_carries_bounded_glob_scan_depth() {
let deny_env_files = FileSystemSandboxEntry {
path: FileSystemPath::GlobPattern {
pattern: "**/*.env".to_string(),
},
access: FileSystemAccessMode::None,
};
let merged_policy = merge_file_system_policy_with_additional_permissions(
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
}]),
&FileSystemPermissions {
entries: vec![deny_env_files.clone()],
glob_scan_max_depth: std::num::NonZeroUsize::new(2),
},
);
assert_eq!(merged_policy, {
let mut policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Write,
},
deny_env_files,
]);
policy.glob_scan_max_depth = Some(2);
policy
});
}
#[test]
fn effective_file_system_sandbox_policy_returns_base_policy_without_additional_permissions() {
let temp_dir = TempDir::new().expect("create temp dir");
@@ -558,6 +558,7 @@ mod tests {
file_system: Some(AdditionalFileSystemPermissions {
read: Some(vec![absolute_path(read_path)]),
write: Some(vec![absolute_path(write_path)]),
glob_scan_max_depth: None,
entries: None,
}),
},
+2
View File
@@ -2601,6 +2601,7 @@ async fn inactive_thread_exec_approval_preserves_context() {
file_system: Some(AdditionalFileSystemPermissions {
read: Some(vec![test_absolute_path("/tmp/read-only")]),
write: Some(vec![test_absolute_path("/tmp/write")]),
glob_scan_max_depth: None,
entries: None,
}),
});
@@ -2708,6 +2709,7 @@ async fn inactive_thread_permissions_approval_preserves_file_system_permissions(
file_system: Some(AdditionalFileSystemPermissions {
read: Some(vec![test_absolute_path("/tmp/read-only")]),
write: Some(vec![test_absolute_path("/tmp/write")]),
glob_scan_max_depth: None,
entries: None,
}),
},
@@ -92,6 +92,7 @@ mod tests {
file_system: Some(codex_app_server_protocol::AdditionalFileSystemPermissions {
read: Some(vec![absolute_path("/tmp/read-only")]),
write: Some(vec![absolute_path("/tmp/write")]),
glob_scan_max_depth: None,
entries: None,
}),
}
@@ -109,6 +110,7 @@ mod tests {
},
access: FileSystemAccessMode::Write,
}],
glob_scan_max_depth: None,
}),
..Default::default()
}),
@@ -117,6 +119,7 @@ mod tests {
file_system: Some(codex_app_server_protocol::AdditionalFileSystemPermissions {
read: None,
write: None,
glob_scan_max_depth: None,
entries: Some(vec![codex_app_server_protocol::FileSystemSandboxEntry {
path: codex_app_server_protocol::FileSystemPath::Special {
value: codex_app_server_protocol::FileSystemSpecialPath::Root,
@@ -1372,6 +1372,7 @@ mod tests {
access: FileSystemAccessMode::None,
},
],
glob_scan_max_depth: None,
}),
..Default::default()
};
@@ -113,6 +113,7 @@ fn app_server_exec_approval_request_preserves_permissions_context() {
file_system: Some(AppServerAdditionalFileSystemPermissions {
read: Some(vec![read_path.clone()]),
write: Some(vec![write_path.clone()]),
glob_scan_max_depth: None,
entries: None,
}),
}),
@@ -163,6 +164,7 @@ fn app_server_request_permissions_preserves_file_system_permissions() {
file_system: Some(AppServerAdditionalFileSystemPermissions {
read: Some(vec![read_path.clone()]),
write: Some(vec![write_path.clone()]),
glob_scan_max_depth: None,
entries: None,
}),
},