Prevent launch context from reaching child processes (#37607)

## Why

Model-reachable child processes should not inherit Codex launch context.

## What changed

- Treat `OPENAI_FEDERATION_RULE_ID` and `OPENAI_IDENTITY_TOKEN_FILE` as non-inheritable environment variables, with case-insensitive matching.
- Remove them after shell environment policy overrides and before spawning commands across execution, MCP, hooks, Git helpers, and remote helper processes.

## Testing

- Cover inherited and explicitly configured variants, including mixed-case names.
- Verify the variables are absent from real child environments and app-server command and process execution.

GitOrigin-RevId: 2535527893985fef0995617f4c5b2462bea7c136
This commit is contained in:
cooper-oai
2026-08-08 16:58:26 +00:00
committed by copyberry
parent 3aae5d885b
commit c4513cb982
25 changed files with 269 additions and 36 deletions
@@ -4,6 +4,44 @@ use crate::config_types::ShellEnvironmentPolicyInherit;
use std::collections::HashMap;
pub const CODEX_THREAD_ID_ENV_VAR: &str = "CODEX_THREAD_ID";
pub const OPENAI_FEDERATION_RULE_ID_ENV_VAR: &str = "OPENAI_FEDERATION_RULE_ID";
pub const OPENAI_IDENTITY_TOKEN_FILE_ENV_VAR: &str = "OPENAI_IDENTITY_TOKEN_FILE";
/// Environment variables that model-reachable child processes must not inherit.
pub const NON_INHERITABLE_ENV_VARS: &[&str] = &[
OPENAI_FEDERATION_RULE_ID_ENV_VAR,
OPENAI_IDENTITY_TOKEN_FILE_ENV_VAR,
];
pub fn is_non_inheritable_env_var(name: &str) -> bool {
NON_INHERITABLE_ENV_VARS
.iter()
.any(|restricted| restricted.eq_ignore_ascii_case(name))
}
/// Configures a child command to omit non-inheritable variables from the
/// process environment and explicit command overrides.
///
/// This prevents accidental propagation of Codex launch context; it is not a
/// filesystem security boundary for the referenced identity-token file.
pub fn scrub_non_inheritable_env_vars(command: &mut std::process::Command) {
let configured_names = command
.get_envs()
.map(|(name, _)| name.to_os_string())
.collect::<Vec<_>>();
for name in NON_INHERITABLE_ENV_VARS {
command.env_remove(name);
}
for name in std::env::vars_os()
.map(|(name, _)| name)
.chain(configured_names)
{
if name.to_str().is_some_and(is_non_inheritable_env_var) {
command.env_remove(name);
}
}
}
/// Construct a shell environment from the supplied process environment and
/// shell-environment policy.
@@ -106,6 +144,10 @@ where
env_map.insert(CODEX_THREAD_ID_ENV_VAR.to_string(), thread_id.to_string());
}
// Restricted launch context cannot be restored through user-provided shell
// environment overrides.
env_map.retain(|name, _| !is_non_inheritable_env_var(name));
env_map
}
@@ -148,6 +190,10 @@ pub const WINDOWS_CORE_ENV_VARS: &[&str] = &[
"PWSH",
];
#[cfg(test)]
#[path = "shell_environment_tests.rs"]
mod tests;
#[cfg(all(test, target_os = "windows"))]
mod windows_tests {
use super::*;