mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
-
This commit is contained in:
@@ -40,7 +40,9 @@ use crate::apply_patch::convert_apply_patch_to_protocol;
|
||||
use crate::client::ModelClient;
|
||||
use crate::client_common::Prompt;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::config::AdminAuditContext;
|
||||
use crate::config::Config;
|
||||
use crate::config::maybe_post_admin_audit_events;
|
||||
use crate::config_types::ShellEnvironmentPolicy;
|
||||
use crate::conversation_history::ConversationHistory;
|
||||
use crate::conversation_manager::InitialHistory;
|
||||
@@ -273,6 +275,7 @@ struct State {
|
||||
pub(crate) struct Session {
|
||||
session_id: Uuid,
|
||||
tx_event: Sender<Event>,
|
||||
config: Arc<Config>,
|
||||
|
||||
/// Manager for external MCP servers/tools.
|
||||
mcp_connection_manager: McpConnectionManager,
|
||||
@@ -469,6 +472,7 @@ impl Session {
|
||||
let sess = Arc::new(Session {
|
||||
session_id,
|
||||
tx_event: tx_event.clone(),
|
||||
config: config.clone(),
|
||||
mcp_connection_manager,
|
||||
session_manager: ExecSessionManager::default(),
|
||||
notify,
|
||||
@@ -778,12 +782,38 @@ impl Session {
|
||||
self.on_exec_command_begin(turn_diff_tracker, begin_ctx.clone())
|
||||
.await;
|
||||
|
||||
let ExecInvokeArgs {
|
||||
params,
|
||||
sandbox_type,
|
||||
sandbox_policy,
|
||||
approval_policy,
|
||||
codex_linux_sandbox_exe,
|
||||
stdout_stream,
|
||||
record_admin_command_event,
|
||||
} = exec_args;
|
||||
|
||||
if record_admin_command_event {
|
||||
maybe_post_admin_audit_events(
|
||||
&self.config,
|
||||
AdminAuditContext {
|
||||
sandbox_policy,
|
||||
approval_policy,
|
||||
cwd: ¶ms.cwd,
|
||||
command: Some(params.command.as_slice()),
|
||||
dangerously_bypass_requested: matches!(approval_policy, AskForApproval::Never),
|
||||
dangerous_mode_justification: None,
|
||||
record_command_event: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
let result = process_exec_tool_call(
|
||||
exec_args.params,
|
||||
exec_args.sandbox_type,
|
||||
exec_args.sandbox_policy,
|
||||
exec_args.codex_linux_sandbox_exe,
|
||||
exec_args.stdout_stream,
|
||||
params,
|
||||
sandbox_type,
|
||||
sandbox_policy,
|
||||
codex_linux_sandbox_exe,
|
||||
stdout_stream,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -2306,8 +2336,10 @@ pub struct ExecInvokeArgs<'a> {
|
||||
pub params: ExecParams,
|
||||
pub sandbox_type: SandboxType,
|
||||
pub sandbox_policy: &'a SandboxPolicy,
|
||||
pub approval_policy: &'a AskForApproval,
|
||||
pub codex_linux_sandbox_exe: &'a Option<PathBuf>,
|
||||
pub stdout_stream: Option<StdoutStream>,
|
||||
pub record_admin_command_event: bool,
|
||||
}
|
||||
|
||||
fn maybe_translate_shell_command(
|
||||
@@ -2497,6 +2529,7 @@ async fn handle_container_exec_with_params(
|
||||
params: params.clone(),
|
||||
sandbox_type,
|
||||
sandbox_policy: &turn_context.sandbox_policy,
|
||||
approval_policy: &turn_context.approval_policy,
|
||||
codex_linux_sandbox_exe: &sess.codex_linux_sandbox_exe,
|
||||
stdout_stream: if exec_command_context.apply_patch.is_some() {
|
||||
None
|
||||
@@ -2507,6 +2540,7 @@ async fn handle_container_exec_with_params(
|
||||
tx_event: sess.tx_event.clone(),
|
||||
})
|
||||
},
|
||||
record_admin_command_event: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
@@ -2634,6 +2668,7 @@ async fn handle_sandbox_error(
|
||||
params,
|
||||
sandbox_type: SandboxType::None,
|
||||
sandbox_policy: &turn_context.sandbox_policy,
|
||||
approval_policy: &turn_context.approval_policy,
|
||||
codex_linux_sandbox_exe: &sess.codex_linux_sandbox_exe,
|
||||
stdout_stream: if exec_command_context.apply_patch.is_some() {
|
||||
None
|
||||
@@ -2644,6 +2679,7 @@ async fn handle_sandbox_error(
|
||||
tx_event: sess.tx_event.clone(),
|
||||
})
|
||||
},
|
||||
record_admin_command_event: true,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -14,6 +14,8 @@ use crate::model_provider_info::built_in_model_providers;
|
||||
use crate::openai_model_info::get_model_info;
|
||||
use crate::protocol::AskForApproval;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use chrono::Utc;
|
||||
use codex_protocol::config_types::ReasoningEffort;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -25,9 +27,7 @@ use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::io::IsTerminal;
|
||||
use std::io::Write;
|
||||
use std::io::{self};
|
||||
use std::io::{self, IsTerminal, Write};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
@@ -46,18 +46,18 @@ const CONFIG_TOML_FILE: &str = "config.toml";
|
||||
|
||||
const DEFAULT_RESPONSES_ORIGINATOR_HEADER: &str = "codex_cli_rs";
|
||||
|
||||
const ADMIN_DANGEROUS_SANDBOX_DISABLED_MESSAGE: &str = "Running Codex with --yolo/--dangerously-bypass-approvals-and-sandbox or --sandbox danger-full-access has been disabled by your administrator. Please contact your system administrator.";
|
||||
const ADMIN_DANGEROUS_SANDBOX_DISABLED_MESSAGE: &str = "Running Codex with --dangerously-bypass-approvals-and-sandbox or --sandbox danger-full-access has been disabled by your administrator. Please contact your system administrator or try: codex --full-auto";
|
||||
|
||||
const ADMIN_DANGEROUS_SANDBOX_DISABLED_PROMPT_LINES: &[&str] = &[
|
||||
"Running Codex with --yolo/--dangerously-bypass-approvals-and-sandbox or",
|
||||
"Running Codex with --dangerously-bypass-approvals-and-sandbox or",
|
||||
"--sandbox danger-full-access has been disabled by your administrator.",
|
||||
"Please contact your system administrator if you believe you need this mode.",
|
||||
"\nPlease contact your system administrator or try: codex --full-auto",
|
||||
];
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const MANAGED_PREFERENCES_APPLICATION_ID: &str = "com.openai.codex";
|
||||
#[cfg(target_os = "macos")]
|
||||
const MANAGED_PREFERENCES_CONFIG_KEY: &str = "config.toml";
|
||||
const MANAGED_PREFERENCES_CONFIG_KEY: &str = "config_toml_base64";
|
||||
|
||||
/// Application configuration loaded from disk and merged with overrides.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -334,10 +334,7 @@ fn join_config_result(
|
||||
}
|
||||
}
|
||||
|
||||
fn read_config_from_path(
|
||||
path: &Path,
|
||||
log_missing_as_info: bool,
|
||||
) -> std::io::Result<Option<TomlValue>> {
|
||||
fn read_config_from_path(path: &Path, log_missing_as_info: bool) -> std::io::Result<Option<TomlValue>> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(contents) => match toml::from_str::<TomlValue>(&contents) {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
@@ -415,8 +412,21 @@ fn load_managed_admin_config_impl() -> std::io::Result<Option<TomlValue>> {
|
||||
|
||||
let value = unsafe { CFString::wrap_under_create_rule(value_ref as _) };
|
||||
let contents = value.to_string();
|
||||
let trimmed = contents.trim();
|
||||
|
||||
match toml::from_str::<TomlValue>(&contents) {
|
||||
let decoded = BASE64_STANDARD
|
||||
.decode(trimmed.as_bytes())
|
||||
.map_err(|err| {
|
||||
tracing::error!("Failed to decode managed preferences as base64: {err}");
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, err)
|
||||
})?;
|
||||
|
||||
let decoded_str = String::from_utf8(decoded).map_err(|err| {
|
||||
tracing::error!("Managed preferences base64 contents were not valid UTF-8: {err}");
|
||||
std::io::Error::new(std::io::ErrorKind::InvalidData, err)
|
||||
})?;
|
||||
|
||||
match toml::from_str::<TomlValue>(&decoded_str) {
|
||||
Ok(parsed) => Ok(Some(parsed)),
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to parse managed preferences TOML: {err}");
|
||||
@@ -433,8 +443,12 @@ fn load_managed_admin_config_impl() -> std::io::Result<Option<TomlValue>> {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AdminAuditContext<'a> {
|
||||
pub sandbox_policy: &'a SandboxPolicy,
|
||||
pub approval_policy: &'a AskForApproval,
|
||||
pub cwd: &'a Path,
|
||||
pub command: Option<&'a [String]>,
|
||||
pub dangerously_bypass_requested: bool,
|
||||
pub dangerous_mode_justification: Option<&'a str>,
|
||||
pub record_command_event: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -461,6 +475,10 @@ struct AdminAuditPayload<'a> {
|
||||
timestamp: &'a str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
justification: Option<&'a str>,
|
||||
approval_policy: &'a str,
|
||||
cwd: &'a str,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
command: Option<&'a [String]>,
|
||||
}
|
||||
|
||||
pub async fn maybe_post_admin_audit_events(config: &Config, context: AdminAuditContext<'_>) {
|
||||
@@ -473,9 +491,12 @@ pub async fn maybe_post_admin_audit_events(config: &Config, context: AdminAuditC
|
||||
}
|
||||
|
||||
let sandbox_mode = context.sandbox_policy.to_string();
|
||||
let approval_policy_display = context.approval_policy.to_string();
|
||||
let cwd_display = context.cwd.display().to_string();
|
||||
let command_args = context.command;
|
||||
let client = Client::new();
|
||||
|
||||
if audit.events.all_commands {
|
||||
if audit.events.all_commands && context.record_command_event {
|
||||
post_admin_audit_event(
|
||||
&client,
|
||||
&audit.endpoint,
|
||||
@@ -483,6 +504,9 @@ pub async fn maybe_post_admin_audit_events(config: &Config, context: AdminAuditC
|
||||
&sandbox_mode,
|
||||
context.dangerously_bypass_requested,
|
||||
context.dangerous_mode_justification,
|
||||
&approval_policy_display,
|
||||
&cwd_display,
|
||||
command_args,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -498,6 +522,9 @@ pub async fn maybe_post_admin_audit_events(config: &Config, context: AdminAuditC
|
||||
&sandbox_mode,
|
||||
context.dangerously_bypass_requested,
|
||||
context.dangerous_mode_justification,
|
||||
&approval_policy_display,
|
||||
&cwd_display,
|
||||
command_args,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -517,8 +544,12 @@ pub async fn audit_admin_run_with_prompt(
|
||||
config,
|
||||
AdminAuditContext {
|
||||
sandbox_policy: &config.sandbox_policy,
|
||||
approval_policy: &config.approval_policy,
|
||||
cwd: &config.cwd,
|
||||
command: None,
|
||||
dangerously_bypass_requested,
|
||||
dangerous_mode_justification: justification.as_deref(),
|
||||
record_command_event: false,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
@@ -537,6 +568,9 @@ async fn post_admin_audit_event(
|
||||
sandbox_mode: &str,
|
||||
dangerously_bypass_requested: bool,
|
||||
justification: Option<&str>,
|
||||
approval_policy: &str,
|
||||
cwd: &str,
|
||||
command: Option<&[String]>,
|
||||
) {
|
||||
let username = whoami::username();
|
||||
let timestamp_string;
|
||||
@@ -549,6 +583,9 @@ async fn post_admin_audit_event(
|
||||
dangerously_bypass_requested,
|
||||
timestamp: ×tamp_string,
|
||||
justification,
|
||||
approval_policy,
|
||||
cwd,
|
||||
command,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -575,45 +612,26 @@ pub fn prompt_for_admin_danger_reason(config: &Config) -> std::io::Result<Option
|
||||
|
||||
let red = "\x1b[31m";
|
||||
let reset = "\x1b[0m";
|
||||
let blue = "\x1b[34m";
|
||||
let mut stderr = io::stderr();
|
||||
writeln!(stderr)?;
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}╔════════════════════════════════════════════════════════════╗{reset}"
|
||||
"{red}╔══════════════════════════════════════════════════════════════╗{reset}"
|
||||
)?;
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}║ ADMINISTRATOR WARNING ║{reset}"
|
||||
"{red}║ DANGEROUS USAGE WARNING ║{reset}"
|
||||
)?;
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}╚════════════════════════════════════════════════════════════╝{reset}"
|
||||
)?;
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}Dangerous sandbox access requires administrator approval.{reset}"
|
||||
"{red}╚══════════════════════════════════════════════════════════════╝{reset}"
|
||||
)?;
|
||||
for line in ADMIN_DANGEROUS_SANDBOX_DISABLED_PROMPT_LINES {
|
||||
writeln!(stderr, "{red}{line}{reset}")?;
|
||||
}
|
||||
if prompt.sandbox {
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}• Requested flag: --sandbox danger-full-access{reset}"
|
||||
)?;
|
||||
}
|
||||
if prompt.dangerously_bypass {
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}• Requested flag: --dangerously-bypass-approvals-and-sandbox{reset}"
|
||||
)?;
|
||||
}
|
||||
writeln!(
|
||||
stderr,
|
||||
"{red}Provide a justification below to continue.{reset}"
|
||||
)?;
|
||||
writeln!(stderr)?;
|
||||
write!(stderr, "{red}?{reset} Justification: ")?;
|
||||
write!(stderr, "{red}?{reset} {blue}Or provide a justification to continue anyway:{reset} ")?;
|
||||
stderr.flush()?;
|
||||
|
||||
let mut input = String::new();
|
||||
@@ -622,7 +640,13 @@ pub fn prompt_for_admin_danger_reason(config: &Config) -> std::io::Result<Option
|
||||
if justification.is_empty() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"An administrator justification is required to proceed.",
|
||||
"Justification is required by your administrator to proceed with dangerous usage.",
|
||||
));
|
||||
}
|
||||
if justification.len() < 4 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Justification must be at least 4 characters long and is required by your administrator to proceed with dangerous usage.",
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user