Honor managed login shell restrictions consistently

This commit is contained in:
Abhinav Vedmala
2026-06-16 06:15:42 +00:00
parent 6427c0c6b7
commit a3edbe96be
5 changed files with 46 additions and 7 deletions
-5
View File
@@ -67,10 +67,6 @@ const RESERVED_MODEL_PROVIDER_IDS: [&str; 4] = [
pub const DEFAULT_PROJECT_DOC_MAX_BYTES: usize = 32 * 1024;
const fn default_allow_login_shell() -> Option<bool> {
Some(true)
}
fn default_history() -> Option<History> {
Some(History::default())
}
@@ -178,7 +174,6 @@ pub struct ConfigToml {
/// If `false`, the model can never use a login shell: `login = true`
/// requests are rejected, and omitting `login` defaults to a non-login
/// shell.
#[serde(default = "default_allow_login_shell")]
pub allow_login_shell: Option<bool>,
/// Sandbox mode to use.
-1
View File
@@ -4407,7 +4407,6 @@
"description": "Agent-related settings (thread limits, etc.)."
},
"allow_login_shell": {
"default": true,
"description": "Whether the model may request a login shell for shell-based tools. Default to `true`\n\nIf `true`, the model may request a login shell (`login = true`), and omitting `login` defaults to using a login shell. If `false`, the model can never use a login shell: `login = true` requests are rejected, and omitting `login` defaults to a non-login shell.",
"type": "boolean"
},
+15
View File
@@ -10928,3 +10928,18 @@ sandbox_private_desktop = false
}));
Ok(())
}
#[tokio::test]
async fn absent_allow_login_shell_does_not_report_an_override() -> std::io::Result<()> {
let codex_home = TempDir::new()?;
let config = load_with_enterprise_requirement(&codex_home, "allow_login_shell = false").await?;
assert!(!config.permissions.allow_login_shell);
assert!(
config
.startup_warnings
.iter()
.all(|warning| !warning.contains("allow_login_shell"))
);
Ok(())
}
+30
View File
@@ -1150,6 +1150,36 @@ async fn user_shell_commands_do_not_inherit_managed_network_proxy() -> anyhow::R
Ok(())
}
#[tokio::test]
async fn user_shell_commands_honor_disabled_login_shells() -> anyhow::Result<()> {
let (session, rx) = make_session_with_config_and_rx(|config| {
config.permissions.allow_login_shell = false;
})
.await?;
let turn_context = session.new_default_turn().await;
let command = "echo managed-login-shell".to_string();
let expected_command = session.user_shell().derive_exec_args(&command, false);
execute_user_shell_command(
Arc::clone(&session),
turn_context,
command,
CancellationToken::new(),
UserShellCommandMode::StandaloneTurn,
)
.await;
loop {
let event = rx.recv().await.expect("channel open");
if let EventMsg::ExecCommandBegin(event) = event.msg {
assert_eq!(event.command, expected_command);
break;
}
}
Ok(())
}
#[tokio::test]
async fn get_base_instructions_no_user_content() {
let prompt_with_apply_patch_instructions =
+1 -1
View File
@@ -142,7 +142,7 @@ pub(crate) async fn execute_user_shell_command(
// Execute the user's script under the environment's shell; this
// allows commands that use shell features (pipes, &&, redirects, etc.).
// We do not source rc files or otherwise reformat the script.
let use_login_shell = true;
let use_login_shell = turn_context.config.permissions.allow_login_shell;
let display_command = environment_shell.derive_exec_args(&command, use_login_shell);
let shell_snapshot_location = turn_environment.shell_snapshot(turn_environment.cwd());
let mut exec_env_map = create_env(