Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
56ae766
Add Windows AppContainer sandbox helper
iceweasel-oai Oct 7, 2025
6207286
windows compilation fixes.
iceweasel-oai Oct 8, 2025
68e0301
rework to avoid unstable raw_attribute_list
iceweasel-oai Oct 8, 2025
d2b8e01
avoid unstable features altogether
iceweasel-oai Oct 8, 2025
47d21f9
simplify to use AppContainerToken.
iceweasel-oai Oct 8, 2025
3d56fcc
hardening tweaks.
iceweasel-oai Oct 8, 2025
a6ed908
build fixes.
iceweasel-oai Oct 8, 2025
207db1e
fix windows crate location
iceweasel-oai Oct 8, 2025
fa2b395
update token generation.
iceweasel-oai Oct 8, 2025
4c6556b
fix build errors.
iceweasel-oai Oct 8, 2025
d89f5c9
extern is always unsafe
iceweasel-oai Oct 8, 2025
be30bfa
fix some clippy issues.
iceweasel-oai Oct 8, 2025
8f2f30d
fix some clippy issues.
iceweasel-oai Oct 8, 2025
5b47f55
give AppContainer perms to TEMP/TMP too
iceweasel-oai Oct 8, 2025
b7441f8
use std::env
iceweasel-oai Oct 8, 2025
a390901
inherit stdout/stderr handles
iceweasel-oai Oct 8, 2025
7b49cc2
inherit stdout/stderr handles
iceweasel-oai Oct 8, 2025
d7dc37d
clippy fix
iceweasel-oai Oct 8, 2025
a9cd8d7
build errors
iceweasel-oai Oct 8, 2025
a1872af
build errors
iceweasel-oai Oct 8, 2025
fd6497c
build errors
iceweasel-oai Oct 8, 2025
663635e
restricted token approach.
iceweasel-oai Oct 9, 2025
6dfc3f5
build errors
iceweasel-oai Oct 9, 2025
b805fe3
build errors
iceweasel-oai Oct 9, 2025
352526d
build errors
iceweasel-oai Oct 9, 2025
7ebbdd4
build errors
iceweasel-oai Oct 9, 2025
4a2d216
build errors
iceweasel-oai Oct 9, 2025
11efab0
build errors
iceweasel-oai Oct 9, 2025
3aa351f
allow read anywhere
iceweasel-oai Oct 9, 2025
def4408
sandbox take 3
iceweasel-oai Oct 10, 2025
6e8b309
windows fixes for v3
iceweasel-oai Oct 10, 2025
1e4b08d
v4
iceweasel-oai Oct 10, 2025
8e82b32
build errors, easier specification of policy
iceweasel-oai Oct 13, 2025
068c4bd
fix warning
iceweasel-oai Oct 13, 2025
fd7b086
gate impl on Windows.
iceweasel-oai Oct 14, 2025
b5acdb5
forgot new windows module.
iceweasel-oai Oct 14, 2025
f3d3a23
restricted v2
iceweasel-oai Oct 14, 2025
51f33d7
restricted python version.
iceweasel-oai Oct 14, 2025
70bda63
updated python v3
iceweasel-oai Oct 15, 2025
ae1cedb
sandbox tests.
iceweasel-oai Oct 15, 2025
92611c9
hookup python as windows sandbox.
iceweasel-oai Oct 16, 2025
dea56b2
get rid of bad rust impl
iceweasel-oai Oct 16, 2025
f9340eb
remove windows-sandbox rust dir
iceweasel-oai Oct 16, 2025
2899af6
add env_utils
iceweasel-oai Oct 16, 2025
9e51d69
python performance improvement.
iceweasel-oai Oct 16, 2025
4ef6766
always print some error when a cmd fails.
iceweasel-oai Oct 16, 2025
4ec5c9f
windows setup ps script.
iceweasel-oai Oct 16, 2025
1133931
latest round of python sandbox improvements.
iceweasel-oai Oct 17, 2025
7f7537e
rust sandbox impl
iceweasel-oai Oct 17, 2025
56824fa
hook up rust sandbox
iceweasel-oai Oct 20, 2025
ff1cfab
use Everyone SID and do directory audit for world-writable directories
iceweasel-oai Oct 21, 2025
507f3f4
prune unused code
iceweasel-oai Oct 21, 2025
38c8f4f
sorta kinda working-ish
iceweasel-oai Oct 22, 2025
b4fa6d4
fix false positives for everyone writable directory detection.
iceweasel-oai Oct 22, 2025
1189941
skip symlinks
iceweasel-oai Oct 22, 2025
b7a7981
fix build issues.
iceweasel-oai Oct 22, 2025
22cb54e
re-hook Windows sandbox
iceweasel-oai Oct 27, 2025
76c86c2
choose WindowsRestrictedToken for Auto sandbox
iceweasel-oai Oct 27, 2025
a5fd614
remove debug code in sandbox.
iceweasel-oai Oct 27, 2025
27e37d3
gate windows sandbox behind experimental flag.
iceweasel-oai Oct 27, 2025
ac8aadd
fix bad import
iceweasel-oai Oct 27, 2025
7189f01
fix patch permissions and quoting. Remove python impl.
iceweasel-oai Oct 28, 2025
4583911
get smoketests up and running again
iceweasel-oai Oct 28, 2025
d4bc18e
cleanup unused code.
iceweasel-oai Oct 28, 2025
f13a938
rename feature to experimental
iceweasel-oai Oct 28, 2025
1fbba53
remove backup file
iceweasel-oai Oct 28, 2025
7be4b04
clean up config gates.
iceweasel-oai Oct 28, 2025
43008db
clean up more config gates.
iceweasel-oai Oct 28, 2025
28d380e
fix windows build issues.
iceweasel-oai Oct 28, 2025
a4f3010
windows build fixes
iceweasel-oai Oct 28, 2025
8e8a4f4
clippy fixes
iceweasel-oai Oct 28, 2025
0d05462
fix clippy warning
iceweasel-oai Oct 28, 2025
0a49a81
fix cargo shear
iceweasel-oai Oct 28, 2025
1c82863
docs update and skip world-writable check
iceweasel-oai Oct 28, 2025
81b7e77
fix error code cast and close parent stdin
iceweasel-oai Oct 28, 2025
dc6218f
fix desktop string quoting
iceweasel-oai Oct 28, 2025
7c6fad1
do not downgrade to read-only if Windows sandbox is enabled
iceweasel-oai Oct 29, 2025
d6d3b7b
hookup sandbox to 'codex sandbox windows'
iceweasel-oai Oct 29, 2025
431b521
remove CLI version of sandbox, in favor of 'codex sandbox windows' an…
iceweasel-oai Oct 29, 2025
9aff097
fix cargo fmt
iceweasel-oai Oct 29, 2025
2ffad0b
update experimental feature name.
iceweasel-oai Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions codex-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ codex-utils-pty = { path = "utils/pty" }
codex-utils-readiness = { path = "utils/readiness" }
codex-utils-string = { path = "utils/string" }
codex-utils-tokenizer = { path = "utils/tokenizer" }
codex-windows-sandbox = { path = "windows-sandbox" }
core_test_support = { path = "core/tests/common" }
mcp-types = { path = "mcp-types" }
mcp_test_support = { path = "mcp-server/tests/common" }
Expand Down Expand Up @@ -210,6 +211,7 @@ walkdir = "2.5.0"
webbrowser = "1.0"
which = "6"
wildmatch = "2.5.0"

wiremock = "0.6"
zeroize = "1.8.1"

Expand Down
3 changes: 3 additions & 0 deletions codex-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ codex sandbox macos [--full-auto] [COMMAND]...
# Linux
codex sandbox linux [--full-auto] [COMMAND]...

# Windows
codex sandbox windows [--full-auto] [COMMAND]...

# Legacy aliases
codex debug seatbelt [--full-auto] [COMMAND]...
codex debug landlock [--full-auto] [COMMAND]...
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ tokio = { workspace = true, features = [
"signal",
] }

[target.'cfg(target_os = "windows")'.dependencies]
codex_windows_sandbox = { package = "codex-windows-sandbox", path = "../windows-sandbox-rs" }

[dev-dependencies]
assert_cmd = { workspace = true }
assert_matches = { workspace = true }
Expand Down
81 changes: 81 additions & 0 deletions codex-rs/cli/src/debug_sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use codex_protocol::config_types::SandboxMode;

use crate::LandlockCommand;
use crate::SeatbeltCommand;
use crate::WindowsCommand;
use crate::exit_status::handle_exit_status;

pub async fn run_command_under_seatbelt(
Expand Down Expand Up @@ -51,9 +52,29 @@ pub async fn run_command_under_landlock(
.await
}

pub async fn run_command_under_windows(
command: WindowsCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
let WindowsCommand {
full_auto,
config_overrides,
command,
} = command;
run_command_under_sandbox(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Windows,
)
.await
}

enum SandboxType {
Seatbelt,
Landlock,
Windows,
}

async fn run_command_under_sandbox(
Expand Down Expand Up @@ -87,6 +108,63 @@ async fn run_command_under_sandbox(
let stdio_policy = StdioPolicy::Inherit;
let env = create_env(&config.shell_environment_policy);

// Special-case Windows sandbox: execute and exit the process to emulate inherited stdio.
if let SandboxType::Windows = sandbox_type {
#[cfg(target_os = "windows")]
{
use codex_windows_sandbox::run_windows_sandbox_capture;

let policy_str = match &config.sandbox_policy {
codex_core::protocol::SandboxPolicy::DangerFullAccess => "workspace-write",
codex_core::protocol::SandboxPolicy::ReadOnly => "read-only",
codex_core::protocol::SandboxPolicy::WorkspaceWrite { .. } => "workspace-write",
};

let sandbox_cwd = sandbox_policy_cwd.clone();
let cwd_clone = cwd.clone();
let env_map = env.clone();
let command_vec = command.clone();
let res = tokio::task::spawn_blocking(move || {
run_windows_sandbox_capture(
policy_str,
&sandbox_cwd,
command_vec,
&cwd_clone,
env_map,
None,
)
})
.await;

let capture = match res {
Ok(Ok(v)) => v,
Ok(Err(err)) => {
eprintln!("windows sandbox failed: {err}");
std::process::exit(1);
}
Err(join_err) => {
eprintln!("windows sandbox join error: {join_err}");
std::process::exit(1);
}
};

if !capture.stdout.is_empty() {
use std::io::Write;
let _ = std::io::stdout().write_all(&capture.stdout);
}
if !capture.stderr.is_empty() {
use std::io::Write;
let _ = std::io::stderr().write_all(&capture.stderr);
}

std::process::exit(capture.exit_code);
}
#[cfg(not(target_os = "windows"))]
{
anyhow::bail!("Windows sandbox is only available on Windows");
}
}

let mut child = match sandbox_type {
SandboxType::Seatbelt => {
spawn_command_under_seatbelt(
Expand Down Expand Up @@ -115,6 +193,9 @@ async fn run_command_under_sandbox(
)
.await?
}
SandboxType::Windows => {
unreachable!("Windows sandbox should have been handled above");
}
};
let status = child.wait().await?;

Expand Down
14 changes: 14 additions & 0 deletions codex-rs/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,17 @@ pub struct LandlockCommand {
#[arg(trailing_var_arg = true)]
pub command: Vec<String>,
}

#[derive(Debug, Parser)]
pub struct WindowsCommand {
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
#[arg(long = "full-auto", default_value_t = false)]
pub full_auto: bool,

#[clap(skip)]
pub config_overrides: CliConfigOverrides,

/// Full command args to run under Windows restricted token sandbox.
#[arg(trailing_var_arg = true)]
pub command: Vec<String>,
}
17 changes: 16 additions & 1 deletion codex-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use codex_chatgpt::apply_command::ApplyCommand;
use codex_chatgpt::apply_command::run_apply_command;
use codex_cli::LandlockCommand;
use codex_cli::SeatbeltCommand;
use codex_cli::WindowsCommand;
use codex_cli::login::read_api_key_from_stdin;
use codex_cli::login::run_login_status;
use codex_cli::login::run_login_with_api_key;
Expand Down Expand Up @@ -151,6 +152,9 @@ enum SandboxCommand {
/// Run a command under Landlock+seccomp (Linux only).
#[clap(visible_alias = "landlock")]
Linux(LandlockCommand),

/// Run a command under Windows restricted token (Windows only).
Windows(WindowsCommand),
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -472,6 +476,17 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
)
.await?;
}
SandboxCommand::Windows(mut windows_cli) => {
prepend_config_flags(
&mut windows_cli.config_overrides,
root_config_overrides.clone(),
);
codex_cli::debug_sandbox::run_command_under_windows(
windows_cli,
codex_linux_sandbox_exe,
)
.await?;
}
},
Some(Subcommand::Apply(mut apply_cli)) => {
prepend_config_flags(
Expand All @@ -497,7 +512,7 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
// Respect root-level `-c` overrides plus top-level flags like `--profile`.
let cli_kv_overrides = root_config_overrides
.parse_overrides()
.map_err(|e| anyhow::anyhow!(e))?;
.map_err(anyhow::Error::msg)?;

// Thread through relevant top-level flags (at minimum, `--profile`).
// Also honor `--search` since it maps to a feature toggle.
Expand Down
24 changes: 18 additions & 6 deletions codex-rs/cli/src/mcp_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ impl McpCli {

async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Result<()> {
// Validate any provided overrides even though they are not currently applied.
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.await
.context("failed to load configuration")?;
Expand Down Expand Up @@ -310,7 +312,9 @@ async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Re
}

async fn run_remove(config_overrides: &CliConfigOverrides, remove_args: RemoveArgs) -> Result<()> {
config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;

let RemoveArgs { name } = remove_args;

Expand Down Expand Up @@ -341,7 +345,9 @@ async fn run_remove(config_overrides: &CliConfigOverrides, remove_args: RemoveAr
}

async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs) -> Result<()> {
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.await
.context("failed to load configuration")?;
Expand Down Expand Up @@ -380,7 +386,9 @@ async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs)
}

async fn run_logout(config_overrides: &CliConfigOverrides, logout_args: LogoutArgs) -> Result<()> {
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.await
.context("failed to load configuration")?;
Expand All @@ -407,7 +415,9 @@ async fn run_logout(config_overrides: &CliConfigOverrides, logout_args: LogoutAr
}

async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) -> Result<()> {
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.await
.context("failed to load configuration")?;
Expand Down Expand Up @@ -662,7 +672,9 @@ async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) ->
}

async fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Result<()> {
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let overrides = config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.await
.context("failed to load configuration")?;
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ tree-sitter-bash = { workspace = true }
uuid = { workspace = true, features = ["serde", "v4"] }
which = { workspace = true }
wildmatch = { workspace = true }
codex_windows_sandbox = { package = "codex-windows-sandbox", path = "../windows-sandbox-rs" }


[target.'cfg(target_os = "linux")'.dependencies]
Expand Down
6 changes: 6 additions & 0 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ impl ConfigToml {
let mut forced_auto_mode_downgraded_on_windows = false;
if cfg!(target_os = "windows")
&& matches!(resolved_sandbox_mode, SandboxMode::WorkspaceWrite)
// If the experimental Windows sandbox is enabled, do not force a downgrade.
&& crate::safety::get_platform_sandbox().is_none()
{
sandbox_policy = SandboxPolicy::new_read_only_policy();
forced_auto_mode_downgraded_on_windows = true;
Expand Down Expand Up @@ -900,6 +902,10 @@ impl Config {
};

let features = Features::from_config(&cfg, &config_profile, feature_overrides);
#[cfg(target_os = "windows")]
{
crate::safety::set_windows_sandbox_enabled(features.enabled(Feature::WindowsSandbox));
}

let resolved_cwd = {
use std::env;
Expand Down
Loading
Loading