From fa5fa8effc5c21340ddefbb892d6086d2978017a Mon Sep 17 00:00:00 2001 From: Thibault Sottiaux Date: Mon, 28 Apr 2025 07:48:38 -0700 Subject: [PATCH] fix: only allow running without sandbox if explicitly marked in safe container (#699) Signed-off-by: Thibault Sottiaux --- codex-cli/Dockerfile | 4 ++ codex-cli/src/cli.tsx | 4 ++ .../src/utils/agent/handle-exec-command.ts | 45 +++++-------------- codex-cli/src/utils/config.ts | 2 + 4 files changed, 20 insertions(+), 35 deletions(-) diff --git a/codex-cli/Dockerfile b/codex-cli/Dockerfile index 4ed3089b..ab41c5ea 100644 --- a/codex-cli/Dockerfile +++ b/codex-cli/Dockerfile @@ -46,6 +46,10 @@ RUN npm install -g codex.tgz \ && rm -rf /usr/local/share/npm-global/lib/node_modules/codex-cli/tests \ && rm -rf /usr/local/share/npm-global/lib/node_modules/codex-cli/docs +# Inside the container we consider the environment already sufficiently locked +# down, therefore instruct Codex CLI to allow running without sandboxing. +ENV CODEX_UNSAFE_ALLOW_NO_SANDBOX=1 + # Copy and set up firewall script as root. USER root COPY scripts/init_firewall.sh /usr/local/bin/ diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 14b6b0fa..4ddd7d49 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -184,6 +184,10 @@ const cli = meow( }, ); +// --------------------------------------------------------------------------- +// Global flag handling +// --------------------------------------------------------------------------- + // Handle 'completion' subcommand before any prompting or API calls if (cli.input[0] === "completion") { const shell = cli.input[1] || "bash"; diff --git a/codex-cli/src/utils/agent/handle-exec-command.ts b/codex-cli/src/utils/agent/handle-exec-command.ts index b4e4ff0d..6cb48016 100644 --- a/codex-cli/src/utils/agent/handle-exec-command.ts +++ b/codex-cli/src/utils/agent/handle-exec-command.ts @@ -1,13 +1,12 @@ -import type { ApplyPatchCommand, ApprovalPolicy } from "../../approvals.js"; -import type { AppConfig } from "../config.js"; import type { CommandConfirmation } from "./agent-loop.js"; +import type { ApplyPatchCommand, ApprovalPolicy } from "../../approvals.js"; import type { ExecInput } from "./sandbox/interface.js"; import type { ResponseInputItem } from "openai/resources/responses/responses.mjs"; import { canAutoApprove } from "../../approvals.js"; import { formatCommandForDisplay } from "../../format-command.js"; import { FullAutoErrorMode } from "../auto-approval-mode.js"; -import { CODEX_UNSAFE_ALLOW_NO_SANDBOX } from "../config.js"; +import { CODEX_UNSAFE_ALLOW_NO_SANDBOX, type AppConfig } from "../config.js"; import { exec, execApplyPatch } from "./exec.js"; import { ReviewDecision } from "./review.js"; import { isLoggingEnabled, log } from "../logger/log.js"; @@ -272,15 +271,6 @@ async function execCommand( }; } -const isInLinux = async (): Promise => { - try { - await access("/proc/1/cgroup"); - return true; - } catch { - return false; - } -}; - /** * Return `true` if the `sandbox-exec` binary can be located. This intentionally does **not** * spawn the binary – we only care about its presence. @@ -305,35 +295,20 @@ async function getSandbox(runInSandbox: boolean): Promise { // instance, inside certain CI images). Attempting to spawn a missing // binary makes Node.js throw an *uncaught* `ENOENT` error further down // the stack which crashes the whole CLI. - - // To provide a graceful degradation path we first check at runtime - // whether `sandbox-exec` can be found **and** is executable. If the - // check fails we fall back to the NONE sandbox while emitting a - // warning so users and maintainers are aware that the additional - // process isolation is not in effect. - if (await isSandboxExecAvailable()) { return SandboxType.MACOS_SEATBELT; - } - - if (CODEX_UNSAFE_ALLOW_NO_SANDBOX) { - log( - "WARNING: macOS Seatbelt sandbox requested but 'sandbox-exec' was not found in PATH. " + - "With `CODEX_UNSAFE_ALLOW_NO_SANDBOX` enabled, continuing without sandbox.", + } else { + throw new Error( + "Sandbox was mandated, but 'sandbox-exec' was not found in PATH!", ); - return SandboxType.NONE; } - } else if (await isInLinux()) { - return SandboxType.NONE; - } else if (process.platform === "win32") { - // On Windows, we don't have a sandbox implementation yet, so we fall back to NONE - // instead of throwing an error, which would crash the application - log( - "WARNING: Sandbox was requested but is not available on Windows. Continuing without sandbox.", - ); + } else if (CODEX_UNSAFE_ALLOW_NO_SANDBOX) { + // Allow running without a sandbox if the user has explicitly marked the + // environment as already being sufficiently locked-down. return SandboxType.NONE; } - // For other platforms, still throw an error as before + + // For all else, we hard fail if the user has requested a sandbox and none is available. throw new Error("Sandbox was mandated, but no sandbox is available!"); } else { return SandboxType.NONE; diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index f778764f..6d7d682b 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -65,6 +65,8 @@ export let OPENAI_API_KEY = process.env["OPENAI_API_KEY"] || ""; export const OPENAI_ORGANIZATION = process.env["OPENAI_ORGANIZATION"] || ""; export const OPENAI_PROJECT = process.env["OPENAI_PROJECT"] || ""; +// Can be set `true` when Codex is running in an environment that is marked as already +// considered sufficiently locked-down so that we allow running wihtout an explicit sandbox. export const CODEX_UNSAFE_ALLOW_NO_SANDBOX = Boolean( process.env["CODEX_UNSAFE_ALLOW_NO_SANDBOX"] || "", );