mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
## Why WSLg's duplicate distro root can expose filesystem contents outside the sandbox's path masks. ## What changed - Detect the duplicate root using filesystem identity, with mount metadata as a fallback, and hide it after applying filesystem grants and denials. - Reject explicit grants, working directories, and executable paths that use the WSLg alias, directing users to the primary filesystem paths. - Hide host procfs when a fresh procfs cannot be mounted so process roots cannot restore access to the masked view. ## Testing Add regression tests for duplicate-root detection, mask ordering, existing ancestor masks, alias rejection, and unrestricted filesystem behavior. Add a WSLg runtime test covering masking with and without fresh procfs, continued access to an allowed file, and rejection of an executable using the alias. GitOrigin-RevId: 9ea31724c00fb24e3a504b6658339e8dfea35343
81 lines
3.3 KiB
Rust
81 lines
3.3 KiB
Rust
//! WSLg runtime coverage for the duplicate-view mask and procfs fallback.
|
|
|
|
use super::NETWORK_TIMEOUT_MS;
|
|
use super::codex_linux_sandbox_exe;
|
|
use super::should_skip_bwrap_tests;
|
|
use codex_protocol::models::PermissionProfile;
|
|
use pretty_assertions::assert_eq;
|
|
use std::os::unix::fs::MetadataExt as _;
|
|
use std::time::Duration;
|
|
|
|
#[tokio::test]
|
|
async fn wslg_duplicate_view_is_masked_with_and_without_fresh_procfs() {
|
|
let root = std::fs::metadata("/").expect("inspect root");
|
|
let has_wslg_root = std::fs::symlink_metadata("/mnt/wslg/distro").is_ok_and(|metadata| {
|
|
metadata.is_dir() && (metadata.dev(), metadata.ino()) == (root.dev(), root.ino())
|
|
});
|
|
if !has_wslg_root || should_skip_bwrap_tests().await {
|
|
eprintln!("skipping WSLg test: WSLg or bwrap prerequisites are unavailable");
|
|
return;
|
|
}
|
|
|
|
let workspace = tempfile::tempdir().expect("create workspace");
|
|
let allowed = workspace.path().join("allowed.txt");
|
|
std::fs::write(&allowed, "allowed\n").expect("write allowed fixture");
|
|
let profile = serde_json::to_string(&PermissionProfile::read_only())
|
|
.expect("serialize read-only profile");
|
|
|
|
for no_proc in [false, true] {
|
|
let mut command = tokio::process::Command::new(codex_linux_sandbox_exe());
|
|
command
|
|
.arg("--sandbox-policy-cwd")
|
|
.arg(workspace.path())
|
|
.args(["--permission-profile", &profile]);
|
|
if no_proc {
|
|
command.arg("--no-proc");
|
|
}
|
|
// Inspect the protection itself, without accessing data through the alias.
|
|
let script = if no_proc {
|
|
"stat -c %a /mnt/wslg/distro && stat -f -c %T /mnt/wslg/distro && cat \"$1\" && test ! -e /proc/1"
|
|
} else {
|
|
"stat -c %a /mnt/wslg/distro && stat -f -c %T /mnt/wslg/distro && cat \"$1\""
|
|
};
|
|
let output = tokio::time::timeout(
|
|
Duration::from_millis(NETWORK_TIMEOUT_MS),
|
|
command
|
|
.args(["--", "/bin/sh", "-c", script, "sh"])
|
|
.arg(&allowed)
|
|
.kill_on_drop(true)
|
|
.output(),
|
|
)
|
|
.await
|
|
.expect("WSLg sandbox check should finish")
|
|
.expect("sandbox helper should start");
|
|
assert!(
|
|
output.status.success(),
|
|
"WSLg mask check failed (no_proc={no_proc}): {}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert_eq!(output.stdout, b"0\ntmpfs\nallowed\n");
|
|
}
|
|
// Exercise the public helper boundary: production wraps the requested
|
|
// executable in another helper command before constructing bwrap args.
|
|
let output = tokio::process::Command::new(codex_linux_sandbox_exe())
|
|
.arg("--sandbox-policy-cwd")
|
|
.arg(workspace.path())
|
|
.args([
|
|
"--permission-profile",
|
|
&profile,
|
|
"--",
|
|
"/mnt/wslg/distro/bin/true",
|
|
])
|
|
.output()
|
|
.await
|
|
.expect("sandbox helper should start");
|
|
assert_eq!(output.status.code(), Some(1));
|
|
assert_eq!(
|
|
String::from_utf8(output.stderr).expect("UTF-8 diagnostic"),
|
|
"error building bubblewrap command: Fatal error: restricted sandboxes do not support paths under /mnt/wslg/distro: /mnt/wslg/distro/bin/true; use the corresponding path under the primary filesystem root instead\n"
|
|
);
|
|
}
|