Migrate legacy exec policy allow rules (#34271)

## What changed

- On session startup, remove exact `allow` entries from `rules/default.rules`
  for command prefixes that Codex no longer suggests as policy amendments.
- Record the migration in `.sandbox_migration` so it runs only once, preserving
  rules created after the migration.
- Skip the migration when user and project exec policy rules are ignored.
- Expand the protected prefix list across shells, interpreters, package runners,
  and destructive or privilege-related commands.

## Testing

- Cover selective removal, case-insensitive matching, one-time behavior, and the
  startup path with ignored policy rules.

GitOrigin-RevId: a0c60e3f82b9630e621fd034b40462e3ab775102
This commit is contained in:
Won Park
2026-07-20 06:13:29 +00:00
committed by copyberry
parent 678157acaa
commit bf3c1972b7
10 changed files with 336 additions and 39 deletions
+80 -38
View File
@@ -50,53 +50,95 @@ const REJECT_RULES_APPROVAL_REASON: &str =
const RULES_DIR_NAME: &str = "rules";
const RULE_EXTENSION: &str = "rules";
const DEFAULT_POLICY_FILE: &str = "default.rules";
static BANNED_PREFIX_SUGGESTIONS: &[&[&str]] = &[
&["python3"],
&["python3", "-"],
&["python3", "-c"],
&["python"],
&["python", "-"],
&["python", "-c"],
&["py"],
&["py", "-3"],
&["pythonw"],
&["pyw"],
&["pypy"],
&["pypy3"],
&["git"],
&["bash"],
&["bash", "-lc"],
&["sh"],
&["sh", "-c"],
&["sh", "-lc"],
&["zsh"],
&["zsh", "-lc"],
&["/bin/zsh"],
&["/bin/zsh", "-lc"],
pub(crate) static BANNED_PREFIX_SUGGESTIONS: &[&[&str]] = &[
&["/bin/bash"],
&["/bin/bash", "-c"],
&["/bin/bash", "-lc"],
&["pwsh"],
&["pwsh", "-Command"],
&["pwsh", "-c"],
&["/bin/sh"],
&["/bin/sh", "-c"],
&["/bin/sh", "-lc"],
&["/bin/zsh"],
&["/bin/zsh", "-c"],
&["/bin/zsh", "-lc"],
&["Rscript"],
&["bash"],
&["bash", "-c"],
&["bash", "-lc"],
&["bun"],
&["bun", "-e"],
&["bun", "run"],
&["cmd"],
&["cmd", "/c"],
&["cmd", "/k"],
&["cmd.exe"],
&["cmd.exe", "/c"],
&["cmd.exe", "/k"],
&["dash"],
&["dash", "-c"],
&["deno"],
&["deno", "eval"],
&["env"],
&["fish"],
&["fish", "-c"],
&["git"],
&["julia"],
&["julia", "-e"],
&["ksh"],
&["ksh", "-c"],
&["lua"],
&["lua", "-e"],
&["node"],
&["node", "-e"],
&["nodejs"],
&["nodejs", "-e"],
&["npm", "run"],
&["osascript"],
&["perl"],
&["perl", "-e"],
&["php"],
&["php", "-r"],
&["pnpm", "run"],
&["powershell"],
&["powershell", "-Command"],
&["powershell", "-EncodedCommand"],
&["powershell", "-File"],
&["powershell", "-c"],
&["powershell.exe"],
&["powershell.exe", "-Command"],
&["powershell.exe", "-EncodedCommand"],
&["powershell.exe", "-File"],
&["powershell.exe", "-c"],
&["env"],
&["sudo"],
&["node"],
&["node", "-e"],
&["perl"],
&["perl", "-e"],
&["pwsh"],
&["pwsh", "-Command"],
&["pwsh", "-EncodedCommand"],
&["pwsh", "-File"],
&["pwsh", "-c"],
&["pwsh", "-e"],
&["pwsh", "-ec"],
&["pwsh", "-f"],
&["py"],
&["py", "-3"],
&["pypy"],
&["pypy3"],
&["python"],
&["python", "-"],
&["python", "-c"],
&["python3"],
&["python3", "-"],
&["python3", "-c"],
&["pythonw"],
&["pyw"],
&["rm"],
&["ruby"],
&["ruby", "-e"],
&["php"],
&["php", "-r"],
&["lua"],
&["lua", "-e"],
&["osascript"],
&["sh"],
&["sh", "-c"],
&["sh", "-lc"],
&["sudo"],
&["yarn", "run"],
&["zsh"],
&["zsh", "-c"],
&["zsh", "-lc"],
];
/// Describes which unmatched-command heuristics should classify the command
@@ -796,7 +838,7 @@ fn profile_has_managed_filesystem_restrictions(permission_profile: &PermissionPr
&& !file_system_sandbox_policy.has_full_disk_write_access()
}
fn default_policy_path(codex_home: &Path) -> PathBuf {
pub(crate) fn default_policy_path(codex_home: &Path) -> PathBuf {
codex_home.join(RULES_DIR_NAME).join(DEFAULT_POLICY_FILE)
}
+1
View File
@@ -1946,6 +1946,7 @@ fn derive_requested_execpolicy_amendment_returns_none_for_shell_and_powershell_v
vec!["pwsh".to_string()],
vec!["pwsh".to_string(), "-Command".to_string()],
vec!["pwsh".to_string(), "-c".to_string()],
vec!["pwsh".to_string(), "-ec".to_string()],
vec!["powershell".to_string()],
vec!["powershell".to_string(), "-Command".to_string()],
vec!["powershell".to_string(), "-c".to_string()],
+19
View File
@@ -33,7 +33,9 @@ use crate::context::world_state::WorldState;
use crate::current_time::TimeProvider;
use crate::default_skill_metadata_budget;
use crate::environment_selection::TurnEnvironmentSnapshot;
use crate::exec_policy::BANNED_PREFIX_SUGGESTIONS;
use crate::exec_policy::ExecPolicyManager;
use crate::exec_policy::default_policy_path;
use crate::image_preparation::prepare_response_items as prepare_image_response_items;
use crate::parse_turn_item;
use crate::realtime_conversation::RealtimeConversationManager;
@@ -57,6 +59,7 @@ use codex_connectors::connector_runtime_context_key;
use codex_core_skills::injection::HostSkillsCatalogInWorldState;
use codex_exec_server::Environment;
use codex_exec_server::EnvironmentManager;
use codex_execpolicy::prefix_rule_migration;
use codex_extension_api::ExtensionDataInit;
use codex_extension_api::LoadedUserInstructions;
use codex_extension_api::PromptFragment;
@@ -549,6 +552,22 @@ impl Session {
} else if let Some(exec_policy) = &inherited_exec_policy {
Arc::clone(exec_policy)
} else {
if !config
.config_layer_stack
.ignore_user_and_project_exec_policy_rules()
{
let codex_home = config.codex_home.clone();
let policy_path = default_policy_path(codex_home.as_path());
if let Err(err) = prefix_rule_migration(
codex_home.as_path(),
policy_path.as_path(),
BANNED_PREFIX_SUGGESTIONS,
)
.await
{
tracing::warn!(error = %err, "failed to run prefix rule migration");
}
}
Arc::new(
ExecPolicyManager::load(&config.config_layer_stack)
.await