mirror of
https://github.com/openai/codex.git
synced 2026-10-21 12:10:06 +00:00
## Why Command processes need thread and tool call labels for correlation, without stale values from inherited environments or cached shell snapshots. ## What changed - Export the originating `exec_command` call ID as `CODEX_TOOL_CALL_ID`; later `write_stdin` calls leave it unchanged. - Apply executor metadata as `CODEX_THREAD_ID` and `CODEX_TOOL_CALL_ID` after environment policies, overlays, and snapshot replay. Clear omitted fields when metadata is present; without metadata, preserve thread context and remove the call ID. - Exclude both IDs from newly captured snapshots and omit the call ID from capture and validation helpers. - Preserve empty call IDs, omit NUL-containing IDs, and handle case-insensitive environment names on Windows. Document that these labels support correlation, not authorization. ## Testing Add regression coverage for metadata precedence, missing and invalid IDs, snapshot capture and replay, local and remote execution with pipes and TTYs, sandboxed remote execution, and preservation of the originating call ID across later input. GitOrigin-RevId: 1027212b5d495d8e7cfac0f498c3134e060aa897
113 lines
4.2 KiB
Rust
113 lines
4.2 KiB
Rust
pub use codex_apply_patch::CODEX_APPLY_PATCH_PRESERVE_LINE_ENDINGS_ENV_VAR;
|
|
use codex_protocol::SessionId;
|
|
use codex_protocol::ThreadId;
|
|
#[cfg(test)]
|
|
use codex_protocol::config_types::EnvironmentVariablePattern;
|
|
use codex_protocol::config_types::ShellEnvironmentPolicy;
|
|
use codex_protocol::models::ActivePermissionProfile;
|
|
use codex_protocol::shell_environment;
|
|
use std::collections::HashMap;
|
|
|
|
pub use codex_protocol::shell_environment::CODEX_SESSION_ID_ENV_VAR;
|
|
pub use codex_protocol::shell_environment::CODEX_THREAD_ID_ENV_VAR;
|
|
pub use codex_protocol::shell_environment::CODEX_TOOL_CALL_ID_ENV_VAR;
|
|
pub(crate) use codex_protocol::shell_environment::set_tool_call_id_env_var;
|
|
|
|
pub(crate) const CODEX_VERSION_ENV_VAR: &str = "CODEX_VERSION";
|
|
|
|
/// Informational name of the active permission profile. Child processes can
|
|
/// overwrite this value, so it must not be treated as proof of enforcement.
|
|
pub const CODEX_PERMISSION_PROFILE_ENV_VAR: &str = "CODEX_PERMISSION_PROFILE";
|
|
|
|
/// Construct an environment map based on the rules in the specified policy. The
|
|
/// resulting map can be passed directly to `Command::envs()` after calling
|
|
/// `env_clear()` to ensure no unintended variables are leaked to the spawned
|
|
/// process.
|
|
///
|
|
/// The derivation follows the algorithm documented in the struct-level comment
|
|
/// for [`ShellEnvironmentPolicy`].
|
|
///
|
|
/// `CODEX_THREAD_ID` is injected when a thread id is provided, even when
|
|
/// `include_only` is set.
|
|
pub fn create_env(
|
|
policy: &ShellEnvironmentPolicy,
|
|
thread_id: Option<ThreadId>,
|
|
) -> HashMap<String, String> {
|
|
let thread_id = thread_id.map(|thread_id| thread_id.to_string());
|
|
shell_environment::create_env(policy, thread_id.as_deref())
|
|
}
|
|
|
|
/// Exposes the shared root-session identity and harness version to shell commands.
|
|
pub(crate) fn inject_session_env(env: &mut HashMap<String, String>, session_id: SessionId) {
|
|
env.insert(CODEX_SESSION_ID_ENV_VAR.to_string(), session_id.to_string());
|
|
if cfg!(windows) {
|
|
env.retain(|key, _| !key.eq_ignore_ascii_case(CODEX_VERSION_ENV_VAR));
|
|
}
|
|
env.insert(
|
|
CODEX_VERSION_ENV_VAR.to_string(),
|
|
env!("CARGO_PKG_VERSION").to_string(),
|
|
);
|
|
}
|
|
|
|
/// Injects the selected named permission profile into a shell tool's environment.
|
|
///
|
|
/// This is applied after the shell environment policy so the runtime-selected
|
|
/// profile wins over inherited or configured values.
|
|
pub(crate) fn inject_permission_profile_env(
|
|
env: &mut HashMap<String, String>,
|
|
active_permission_profile: Option<&ActivePermissionProfile>,
|
|
) {
|
|
if cfg!(windows) {
|
|
env.retain(|key, _| !key.eq_ignore_ascii_case(CODEX_PERMISSION_PROFILE_ENV_VAR));
|
|
} else {
|
|
env.remove(CODEX_PERMISSION_PROFILE_ENV_VAR);
|
|
}
|
|
if let Some(active_permission_profile) = active_permission_profile {
|
|
env.insert(
|
|
CODEX_PERMISSION_PROFILE_ENV_VAR.to_string(),
|
|
active_permission_profile.id.clone(),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Enables line-ending preservation for older standalone patch executables,
|
|
/// including those provided by remote executors. Override inherited values so
|
|
/// they cannot restore the legacy normalization behavior.
|
|
pub fn inject_apply_patch_env(env: &mut HashMap<String, String>) {
|
|
env.retain(|key, _| !key.eq_ignore_ascii_case(CODEX_APPLY_PATCH_PRESERVE_LINE_ENDINGS_ENV_VAR));
|
|
env.insert(
|
|
CODEX_APPLY_PATCH_PRESERVE_LINE_ENDINGS_ENV_VAR.to_string(),
|
|
"1".to_string(),
|
|
);
|
|
}
|
|
|
|
#[cfg(all(test, target_os = "windows"))]
|
|
fn create_env_from_vars<I>(
|
|
vars: I,
|
|
policy: &ShellEnvironmentPolicy,
|
|
thread_id: Option<ThreadId>,
|
|
) -> HashMap<String, String>
|
|
where
|
|
I: IntoIterator<Item = (String, String)>,
|
|
{
|
|
let thread_id = thread_id.map(|thread_id| thread_id.to_string());
|
|
shell_environment::create_env_from_vars(vars, policy, thread_id.as_deref())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn populate_env<I>(
|
|
vars: I,
|
|
policy: &ShellEnvironmentPolicy,
|
|
thread_id: Option<ThreadId>,
|
|
) -> HashMap<String, String>
|
|
where
|
|
I: IntoIterator<Item = (String, String)>,
|
|
{
|
|
let thread_id = thread_id.map(|thread_id| thread_id.to_string());
|
|
shell_environment::populate_env(vars, policy, thread_id.as_deref())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "exec_env_tests.rs"]
|
|
mod tests;
|