mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
## What changed Replace external `rg` scans and the fallback walker with an in-process `ignore` walker for bubblewrap deny-glob expansion. Remove scanner lookup and dependency tracking from sandbox integrity checks. Include hidden and ignored files, respect scan depth limits, and fail closed on traversal errors. Anchor patterns to their static search roots and preserve literal leading `#`, leading `!`, trailing spaces, and unclosed character classes. Follow explicit symlink roots while skipping child symlinks, and mask canonical targets of matched paths. ## Testing Extend unit and sandbox integration coverage for glob syntax, depth limits, traversal failures, symlink handling, commands launched from `/`, and operation without executing `rg`. GitOrigin-RevId: 1767d7f37acb149457642bf24ee5971a28696438
239 lines
8.6 KiB
Rust
239 lines
8.6 KiB
Rust
//! Regression coverage for excluding WSLg's duplicate filesystem view.
|
|
|
|
use super::*;
|
|
use codex_protocol::protocol::FileSystemSandboxEntry;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test_case::test_case(FileSystemAccessMode::Read; "read_root")]
|
|
#[test_case::test_case(FileSystemAccessMode::Write; "write_root")]
|
|
fn wslg_mask_follows_filesystem_grants_and_denials(root_access: FileSystemAccessMode) {
|
|
let temp_dir = tempfile::TempDir::new().expect("temp dir");
|
|
let denied = temp_dir.path().join("denied.txt");
|
|
fs::write(&denied, "fixture").expect("write fixture");
|
|
let denied = AbsolutePathBuf::from_absolute_path(denied).expect("absolute fixture");
|
|
let workspace =
|
|
AbsolutePathBuf::from_absolute_path(temp_dir.path()).expect("absolute workspace");
|
|
|
|
for denied_path in [
|
|
FileSystemPath::from(denied.clone()),
|
|
FileSystemPath::GlobPattern {
|
|
pattern: format!("{}/*.txt", temp_dir.path().display()),
|
|
},
|
|
] {
|
|
let policy = FileSystemSandboxPolicy::restricted(vec![
|
|
FileSystemSandboxEntry {
|
|
path: FileSystemPath::Special {
|
|
value: FileSystemSpecialPath::Root,
|
|
},
|
|
access: root_access,
|
|
missing_path_behavior: None,
|
|
},
|
|
FileSystemSandboxEntry {
|
|
path: workspace.clone().into(),
|
|
access: FileSystemAccessMode::Write,
|
|
missing_path_behavior: None,
|
|
},
|
|
FileSystemSandboxEntry {
|
|
path: denied_path,
|
|
access: FileSystemAccessMode::Deny,
|
|
missing_path_behavior: None,
|
|
},
|
|
]);
|
|
for mount_proc in [true, false] {
|
|
for network_mode in [BwrapNetworkMode::FullAccess, BwrapNetworkMode::Isolated] {
|
|
let args = create_bwrap_command_args(
|
|
vec!["/bin/true".to_string()],
|
|
&policy,
|
|
temp_dir.path(),
|
|
temp_dir.path(),
|
|
BwrapOptions {
|
|
mount_proc,
|
|
network_mode,
|
|
mask_wslg_distro: true,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.expect("create restricted bwrap args")
|
|
.args;
|
|
let mask = args
|
|
.windows(6)
|
|
.position(|args| {
|
|
args == [
|
|
"--perms",
|
|
"000",
|
|
"--tmpfs",
|
|
WSLG_DISTRO_ROOT,
|
|
"--remount-ro",
|
|
WSLG_DISTRO_ROOT,
|
|
]
|
|
})
|
|
.expect("unreadable, read-only WSLg mask");
|
|
let last_grant = args
|
|
.iter()
|
|
.rposition(|arg| arg == "--bind" || arg == "--ro-bind")
|
|
.expect("filesystem grant");
|
|
let denial = args
|
|
.iter()
|
|
.rposition(|arg| arg == &path_to_string(denied.as_path()))
|
|
.expect("denied file mask");
|
|
assert!(last_grant < mask && denial < mask);
|
|
assert!(args.windows(2).any(|args| {
|
|
args == [if mount_proc { "--proc" } else { "--tmpfs" }, "/proc"]
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn wslg_mask_is_omitted_when_policy_already_hides_an_ancestor() {
|
|
for denied in ["/mnt", "/mnt/wslg", WSLG_DISTRO_ROOT] {
|
|
let mut policy = FileSystemSandboxPolicy::read_only();
|
|
policy.entries.push(FileSystemSandboxEntry {
|
|
path: AbsolutePathBuf::from_absolute_path(denied)
|
|
.expect("absolute denied directory")
|
|
.into(),
|
|
access: FileSystemAccessMode::Deny,
|
|
missing_path_behavior: None,
|
|
});
|
|
let mut commands = Vec::new();
|
|
for mask_wslg_distro in [false, true] {
|
|
commands.push(
|
|
create_bwrap_command_args(
|
|
vec!["/bin/true".to_string()],
|
|
&policy,
|
|
Path::new("/"),
|
|
Path::new("/"),
|
|
BwrapOptions {
|
|
mask_wslg_distro,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.expect("create bwrap args with denied ancestor")
|
|
.args,
|
|
);
|
|
}
|
|
assert_eq!(commands[0], commands[1]);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unrestricted_filesystem_preserves_wslg_with_or_without_network_isolation() {
|
|
for network_mode in [BwrapNetworkMode::FullAccess, BwrapNetworkMode::Isolated] {
|
|
let mut commands = Vec::new();
|
|
for mask_wslg_distro in [false, true] {
|
|
commands.push(
|
|
create_bwrap_command_args(
|
|
vec!["/bin/true".to_string()],
|
|
&FileSystemSandboxPolicy::unrestricted(),
|
|
Path::new("/"),
|
|
Path::new("/"),
|
|
BwrapOptions {
|
|
network_mode,
|
|
mask_wslg_distro,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.expect("create unrestricted bwrap args")
|
|
.args,
|
|
);
|
|
}
|
|
assert_eq!(commands[0], commands[1]);
|
|
}
|
|
}
|
|
|
|
#[test_case::test_case("grant_read"; "read_grant")]
|
|
#[test_case::test_case("grant_write"; "write_grant")]
|
|
#[test_case::test_case("policy_cwd"; "policy_cwd")]
|
|
#[test_case::test_case("command_cwd"; "command_cwd")]
|
|
#[test_case::test_case("executable"; "executable")]
|
|
fn explicit_wslg_alias_paths_are_rejected(source: &str) {
|
|
let alias = Path::new(WSLG_DISTRO_ROOT).join("project");
|
|
let mut policy = FileSystemSandboxPolicy::read_only();
|
|
if source.starts_with("grant_") {
|
|
policy.entries.push(FileSystemSandboxEntry {
|
|
path: AbsolutePathBuf::from_absolute_path(&alias)
|
|
.expect("absolute alias")
|
|
.into(),
|
|
access: if source == "grant_write" {
|
|
FileSystemAccessMode::Write
|
|
} else {
|
|
FileSystemAccessMode::Read
|
|
},
|
|
missing_path_behavior: None,
|
|
});
|
|
}
|
|
let executable = if source == "executable" {
|
|
alias.as_path()
|
|
} else {
|
|
Path::new("/bin/true")
|
|
};
|
|
let policy_cwd = if source == "policy_cwd" {
|
|
alias.as_path()
|
|
} else {
|
|
Path::new("/")
|
|
};
|
|
let command_cwd = if source == "command_cwd" {
|
|
alias.as_path()
|
|
} else {
|
|
Path::new("/")
|
|
};
|
|
let error = create_bwrap_command_args(
|
|
vec![path_to_string(executable)],
|
|
&policy,
|
|
policy_cwd,
|
|
command_cwd,
|
|
BwrapOptions {
|
|
mask_wslg_distro: true,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.expect_err("explicit alias must be rejected before mounts are built");
|
|
assert_eq!(
|
|
error.to_string(),
|
|
format!(
|
|
"Fatal error: restricted sandboxes do not support paths under {WSLG_DISTRO_ROOT}: {}; use the corresponding path under the primary filesystem root instead",
|
|
alias.display()
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn wslg_masks_remain_when_glob_skips_directory_symlinks() {
|
|
let temp = tempfile::tempdir().expect("temp directory");
|
|
// '/' is an ancestor on every test host; no WSL mount is needed. Only
|
|
// inspect arguments: never launch this synthetic filesystem policy.
|
|
let relative_root: PathBuf =
|
|
std::iter::repeat_n("..", temp.path().components().count() - 1).collect();
|
|
std::os::unix::fs::symlink(relative_root, temp.path().join("alias")).expect("relative symlink");
|
|
let mut policy = FileSystemSandboxPolicy::read_only();
|
|
policy.entries.push(FileSystemSandboxEntry {
|
|
path: FileSystemPath::GlobPattern {
|
|
pattern: format!("{}/alias*", temp.path().display()),
|
|
},
|
|
access: FileSystemAccessMode::Deny,
|
|
missing_path_behavior: None,
|
|
});
|
|
let args = create_bwrap_command_args(
|
|
vec!["/bin/true".to_string()],
|
|
&policy,
|
|
Path::new("/"),
|
|
Path::new("/"),
|
|
BwrapOptions {
|
|
mask_wslg_distro: true,
|
|
mount_proc: false,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.expect("skipped glob links still need the WSLg mask")
|
|
.args;
|
|
assert!(args.windows(4).any(|args| args
|
|
== [
|
|
"--tmpfs",
|
|
WSLG_DISTRO_ROOT,
|
|
"--remount-ro",
|
|
WSLG_DISTRO_ROOT
|
|
]));
|
|
assert!(args.windows(2).any(|args| args == ["--tmpfs", "/proc"]));
|
|
}
|