Skip to content

Commit 5597925

Browse files
authored
feat(cli): add sandbox profile config controls (#20118)
## Why The explicit profile path from #20117 is meant for standalone testing, but it still inherited the shell cwd and all managed requirements implicitly. The pre-existing launcher path even called out that it did not support a separate cwd yet in [`debug_sandbox.rs`](https://github.com/openai/codex/blob/509453f688a30929432be866402d1ea46aa12169/codex-rs/cli/src/debug_sandbox.rs#L174-L179). For a standalone command, the useful default is to let the caller choose the project directory being tested and to avoid administrator-provided constraints unless the caller explicitly wants to test those too. ## What changed - Add explicit-profile-only `-C/--cd DIR`, and use that cwd for both profile resolution and command execution. - Add explicit-profile-only `--include-managed-config`. - Make explicit profile mode skip managed requirement sources by default, including cloud requirements, MDM requirements, `/etc/codex/requirements.toml`, and the legacy managed-config requirements projection. - Preserve all existing invocations outside the explicit-profile path. ## Stack 1. #20117 `sandbox-ui-profile` 2. #20118 `sandbox-ui-config` --> this PR Both PRs are additive. Replay JSON is intentionally deferred to a follow-up design pass. ## Tests ran - `cargo test -p codex-cli debug_sandbox` - `cargo test -p codex-cli sandbox_macos_` - `cargo test -p codex-core load_config_layers_can_ignore_managed_requirements` - `cargo test -p codex-core load_config_layers_includes_cloud_requirements` - macOS branch-binary smoke on the rebased top of stack: `-C` changed execution cwd, explicit profile mode omitted managed proxy env under `env -i`, and `--include-managed-config` restored it. - Linux devbox branch-binary smoke on the rebased top of stack: `-C` changed execution cwd for built-in and user-defined explicit profiles.
1 parent 857146b commit 5597925

6 files changed

Lines changed: 284 additions & 37 deletions

File tree

codex-rs/cli/src/debug_sandbox.rs

Lines changed: 140 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod seatbelt;
66
use std::path::PathBuf;
77
use std::process::Stdio;
88

9+
use codex_config::LoaderOverrides;
910
use codex_core::config::Config;
1011
use codex_core::config::ConfigBuilder;
1112
use codex_core::config::ConfigOverrides;
@@ -43,13 +44,23 @@ pub async fn run_command_under_seatbelt(
4344
) -> anyhow::Result<()> {
4445
let SeatbeltCommand {
4546
permissions_profile,
47+
cwd,
48+
include_managed_config,
4649
allow_unix_sockets,
4750
log_denials,
4851
config_overrides,
4952
command,
5053
} = command;
54+
let managed_requirements_mode = ManagedRequirementsMode::for_profile_invocation(
55+
&permissions_profile,
56+
include_managed_config,
57+
);
5158
run_command_under_sandbox(
52-
permissions_profile,
59+
DebugSandboxConfigOptions {
60+
permissions_profile,
61+
cwd,
62+
managed_requirements_mode,
63+
},
5364
command,
5465
config_overrides,
5566
codex_linux_sandbox_exe,
@@ -74,11 +85,21 @@ pub async fn run_command_under_landlock(
7485
) -> anyhow::Result<()> {
7586
let LandlockCommand {
7687
permissions_profile,
88+
cwd,
89+
include_managed_config,
7790
config_overrides,
7891
command,
7992
} = command;
93+
let managed_requirements_mode = ManagedRequirementsMode::for_profile_invocation(
94+
&permissions_profile,
95+
include_managed_config,
96+
);
8097
run_command_under_sandbox(
81-
permissions_profile,
98+
DebugSandboxConfigOptions {
99+
permissions_profile,
100+
cwd,
101+
managed_requirements_mode,
102+
},
82103
command,
83104
config_overrides,
84105
codex_linux_sandbox_exe,
@@ -95,11 +116,21 @@ pub async fn run_command_under_windows(
95116
) -> anyhow::Result<()> {
96117
let WindowsCommand {
97118
permissions_profile,
119+
cwd,
120+
include_managed_config,
98121
config_overrides,
99122
command,
100123
} = command;
124+
let managed_requirements_mode = ManagedRequirementsMode::for_profile_invocation(
125+
&permissions_profile,
126+
include_managed_config,
127+
);
101128
run_command_under_sandbox(
102-
permissions_profile,
129+
DebugSandboxConfigOptions {
130+
permissions_profile,
131+
cwd,
132+
managed_requirements_mode,
133+
},
103134
command,
104135
config_overrides,
105136
codex_linux_sandbox_exe,
@@ -117,8 +148,34 @@ enum SandboxType {
117148
Windows,
118149
}
119150

120-
async fn run_command_under_sandbox(
151+
#[derive(Debug)]
152+
struct DebugSandboxConfigOptions {
121153
permissions_profile: Option<String>,
154+
cwd: Option<PathBuf>,
155+
managed_requirements_mode: ManagedRequirementsMode,
156+
}
157+
158+
#[derive(Debug, Clone, Copy)]
159+
enum ManagedRequirementsMode {
160+
Include,
161+
Ignore,
162+
}
163+
164+
impl ManagedRequirementsMode {
165+
fn for_profile_invocation(
166+
permissions_profile: &Option<String>,
167+
include_managed_config: bool,
168+
) -> Self {
169+
if permissions_profile.is_some() && !include_managed_config {
170+
Self::Ignore
171+
} else {
172+
Self::Include
173+
}
174+
}
175+
}
176+
177+
async fn run_command_under_sandbox(
178+
config_options: DebugSandboxConfigOptions,
122179
command: Vec<String>,
123180
config_overrides: CliConfigOverrides,
124181
codex_linux_sandbox_exe: Option<PathBuf>,
@@ -132,7 +189,7 @@ async fn run_command_under_sandbox(
132189
.parse_overrides()
133190
.map_err(anyhow::Error::msg)?,
134191
codex_linux_sandbox_exe,
135-
permissions_profile,
192+
config_options,
136193
)
137194
.await?;
138195

@@ -571,12 +628,12 @@ mod windows_stdio_bridge {
571628
async fn load_debug_sandbox_config(
572629
cli_overrides: Vec<(String, TomlValue)>,
573630
codex_linux_sandbox_exe: Option<PathBuf>,
574-
permissions_profile: Option<String>,
631+
options: DebugSandboxConfigOptions,
575632
) -> anyhow::Result<Config> {
576633
load_debug_sandbox_config_with_codex_home(
577634
cli_overrides,
578635
codex_linux_sandbox_exe,
579-
permissions_profile,
636+
options,
580637
/*codex_home*/ None,
581638
)
582639
.await
@@ -585,9 +642,15 @@ async fn load_debug_sandbox_config(
585642
async fn load_debug_sandbox_config_with_codex_home(
586643
mut cli_overrides: Vec<(String, TomlValue)>,
587644
codex_linux_sandbox_exe: Option<PathBuf>,
588-
permissions_profile: Option<String>,
645+
options: DebugSandboxConfigOptions,
589646
codex_home: Option<PathBuf>,
590647
) -> anyhow::Result<Config> {
648+
let DebugSandboxConfigOptions {
649+
permissions_profile,
650+
cwd,
651+
managed_requirements_mode,
652+
} = options;
653+
591654
if let Some(permissions_profile) = permissions_profile {
592655
cli_overrides.push((
593656
"default_permissions".to_string(),
@@ -604,10 +667,12 @@ async fn load_debug_sandbox_config_with_codex_home(
604667
let config = build_debug_sandbox_config(
605668
cli_overrides.clone(),
606669
ConfigOverrides {
670+
cwd: cwd.clone(),
607671
codex_linux_sandbox_exe: codex_linux_sandbox_exe.clone(),
608672
..Default::default()
609673
},
610674
codex_home.clone(),
675+
managed_requirements_mode,
611676
)
612677
.await?;
613678

@@ -619,10 +684,12 @@ async fn load_debug_sandbox_config_with_codex_home(
619684
cli_overrides,
620685
ConfigOverrides {
621686
sandbox_mode: Some(SandboxMode::ReadOnly),
687+
cwd,
622688
codex_linux_sandbox_exe,
623689
..Default::default()
624690
},
625691
codex_home,
692+
managed_requirements_mode,
626693
)
627694
.await
628695
.map_err(Into::into)
@@ -632,10 +699,17 @@ async fn build_debug_sandbox_config(
632699
cli_overrides: Vec<(String, TomlValue)>,
633700
harness_overrides: ConfigOverrides,
634701
codex_home: Option<PathBuf>,
702+
managed_requirements_mode: ManagedRequirementsMode,
635703
) -> std::io::Result<Config> {
636704
let mut builder = ConfigBuilder::default()
637705
.cli_overrides(cli_overrides)
638706
.harness_overrides(harness_overrides);
707+
if let ManagedRequirementsMode::Ignore = managed_requirements_mode {
708+
builder = builder.loader_overrides(LoaderOverrides {
709+
ignore_managed_requirements: true,
710+
..Default::default()
711+
});
712+
}
639713
if let Some(codex_home) = codex_home {
640714
builder = builder
641715
.codex_home(codex_home.clone())
@@ -701,6 +775,7 @@ mod tests {
701775
Vec::new(),
702776
ConfigOverrides::default(),
703777
Some(codex_home_path.clone()),
778+
ManagedRequirementsMode::Include,
704779
)
705780
.await?;
706781
let legacy_config = build_debug_sandbox_config(
@@ -710,13 +785,18 @@ mod tests {
710785
..Default::default()
711786
},
712787
Some(codex_home_path.clone()),
788+
ManagedRequirementsMode::Include,
713789
)
714790
.await?;
715791

716792
let config = load_debug_sandbox_config_with_codex_home(
717793
Vec::new(),
718794
/*codex_linux_sandbox_exe*/ None,
719-
/*permissions_profile*/ None,
795+
DebugSandboxConfigOptions {
796+
permissions_profile: None,
797+
cwd: None,
798+
managed_requirements_mode: ManagedRequirementsMode::Include,
799+
},
720800
Some(codex_home_path),
721801
)
722802
.await?;
@@ -752,6 +832,7 @@ mod tests {
752832
cli_overrides.clone(),
753833
ConfigOverrides::default(),
754834
Some(codex_home_path.clone()),
835+
ManagedRequirementsMode::Include,
755836
)
756837
.await?;
757838
let read_only_config = build_debug_sandbox_config(
@@ -761,13 +842,18 @@ mod tests {
761842
..Default::default()
762843
},
763844
Some(codex_home_path.clone()),
845+
ManagedRequirementsMode::Include,
764846
)
765847
.await?;
766848

767849
let config = load_debug_sandbox_config_with_codex_home(
768850
cli_overrides,
769851
/*codex_linux_sandbox_exe*/ None,
770-
/*permissions_profile*/ None,
852+
DebugSandboxConfigOptions {
853+
permissions_profile: None,
854+
cwd: None,
855+
managed_requirements_mode: ManagedRequirementsMode::Include,
856+
},
771857
Some(codex_home_path),
772858
)
773859
.await?;
@@ -811,13 +897,18 @@ mod tests {
811897
..Default::default()
812898
},
813899
Some(codex_home_path.clone()),
900+
ManagedRequirementsMode::Include,
814901
)
815902
.await?;
816903

817904
let config = load_debug_sandbox_config_with_codex_home(
818905
Vec::new(),
819906
/*codex_linux_sandbox_exe*/ None,
820-
/*permissions_profile*/ None,
907+
DebugSandboxConfigOptions {
908+
permissions_profile: None,
909+
cwd: None,
910+
managed_requirements_mode: ManagedRequirementsMode::Include,
911+
},
821912
Some(codex_home_path),
822913
)
823914
.await?;
@@ -838,7 +929,11 @@ mod tests {
838929
let config = load_debug_sandbox_config_with_codex_home(
839930
Vec::new(),
840931
/*codex_linux_sandbox_exe*/ None,
841-
Some(":workspace".to_string()),
932+
DebugSandboxConfigOptions {
933+
permissions_profile: Some(":workspace".to_string()),
934+
cwd: None,
935+
managed_requirements_mode: ManagedRequirementsMode::Ignore,
936+
},
842937
Some(codex_home.path().to_path_buf()),
843938
)
844939
.await?;
@@ -867,7 +962,11 @@ mod tests {
867962
let config = load_debug_sandbox_config_with_codex_home(
868963
Vec::new(),
869964
/*codex_linux_sandbox_exe*/ None,
870-
Some(":workspace".to_string()),
965+
DebugSandboxConfigOptions {
966+
permissions_profile: Some(":workspace".to_string()),
967+
cwd: None,
968+
managed_requirements_mode: ManagedRequirementsMode::Ignore,
969+
},
871970
Some(codex_home.path().to_path_buf()),
872971
)
873972
.await?;
@@ -892,7 +991,11 @@ mod tests {
892991
let config = load_debug_sandbox_config_with_codex_home(
893992
Vec::new(),
894993
/*codex_linux_sandbox_exe*/ None,
895-
Some("limited-read-test".to_string()),
994+
DebugSandboxConfigOptions {
995+
permissions_profile: Some("limited-read-test".to_string()),
996+
cwd: None,
997+
managed_requirements_mode: ManagedRequirementsMode::Ignore,
998+
},
896999
Some(codex_home.path().to_path_buf()),
8971000
)
8981001
.await?;
@@ -904,6 +1007,7 @@ mod tests {
9041007
)],
9051008
ConfigOverrides::default(),
9061009
Some(codex_home.path().to_path_buf()),
1010+
ManagedRequirementsMode::Include,
9071011
)
9081012
.await?;
9091013

@@ -914,4 +1018,26 @@ mod tests {
9141018

9151019
Ok(())
9161020
}
1021+
1022+
#[tokio::test]
1023+
async fn debug_sandbox_uses_explicit_profile_cwd() -> anyhow::Result<()> {
1024+
let codex_home = TempDir::new()?;
1025+
let cwd = TempDir::new()?;
1026+
1027+
let config = load_debug_sandbox_config_with_codex_home(
1028+
Vec::new(),
1029+
/*codex_linux_sandbox_exe*/ None,
1030+
DebugSandboxConfigOptions {
1031+
permissions_profile: Some(":workspace".to_string()),
1032+
cwd: Some(cwd.path().to_path_buf()),
1033+
managed_requirements_mode: ManagedRequirementsMode::Ignore,
1034+
},
1035+
Some(codex_home.path().to_path_buf()),
1036+
)
1037+
.await?;
1038+
1039+
assert_eq!(config.cwd.as_path(), cwd.path());
1040+
1041+
Ok(())
1042+
}
9171043
}

0 commit comments

Comments
 (0)