fix: only allow running without sandbox if explicitly marked in safe container (#699)

Signed-off-by: Thibault Sottiaux <tibo@openai.com>
This commit is contained in:
Thibault Sottiaux
2025-04-28 07:48:38 -07:00
committed by GitHub
parent 4eda4dd772
commit fa5fa8effc
4 changed files with 20 additions and 35 deletions

View File

@@ -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/tests \
&& rm -rf /usr/local/share/npm-global/lib/node_modules/codex-cli/docs && 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. # Copy and set up firewall script as root.
USER root USER root
COPY scripts/init_firewall.sh /usr/local/bin/ COPY scripts/init_firewall.sh /usr/local/bin/

View File

@@ -184,6 +184,10 @@ const cli = meow(
}, },
); );
// ---------------------------------------------------------------------------
// Global flag handling
// ---------------------------------------------------------------------------
// Handle 'completion' subcommand before any prompting or API calls // Handle 'completion' subcommand before any prompting or API calls
if (cli.input[0] === "completion") { if (cli.input[0] === "completion") {
const shell = cli.input[1] || "bash"; const shell = cli.input[1] || "bash";

View File

@@ -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 { CommandConfirmation } from "./agent-loop.js";
import type { ApplyPatchCommand, ApprovalPolicy } from "../../approvals.js";
import type { ExecInput } from "./sandbox/interface.js"; import type { ExecInput } from "./sandbox/interface.js";
import type { ResponseInputItem } from "openai/resources/responses/responses.mjs"; import type { ResponseInputItem } from "openai/resources/responses/responses.mjs";
import { canAutoApprove } from "../../approvals.js"; import { canAutoApprove } from "../../approvals.js";
import { formatCommandForDisplay } from "../../format-command.js"; import { formatCommandForDisplay } from "../../format-command.js";
import { FullAutoErrorMode } from "../auto-approval-mode.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 { exec, execApplyPatch } from "./exec.js";
import { ReviewDecision } from "./review.js"; import { ReviewDecision } from "./review.js";
import { isLoggingEnabled, log } from "../logger/log.js"; import { isLoggingEnabled, log } from "../logger/log.js";
@@ -272,15 +271,6 @@ async function execCommand(
}; };
} }
const isInLinux = async (): Promise<boolean> => {
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** * Return `true` if the `sandbox-exec` binary can be located. This intentionally does **not**
* spawn the binary we only care about its presence. * spawn the binary we only care about its presence.
@@ -305,35 +295,20 @@ async function getSandbox(runInSandbox: boolean): Promise<SandboxType> {
// instance, inside certain CI images). Attempting to spawn a missing // instance, inside certain CI images). Attempting to spawn a missing
// binary makes Node.js throw an *uncaught* `ENOENT` error further down // binary makes Node.js throw an *uncaught* `ENOENT` error further down
// the stack which crashes the whole CLI. // 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()) { if (await isSandboxExecAvailable()) {
return SandboxType.MACOS_SEATBELT; return SandboxType.MACOS_SEATBELT;
} } else {
throw new Error(
if (CODEX_UNSAFE_ALLOW_NO_SANDBOX) { "Sandbox was mandated, but 'sandbox-exec' was not found in PATH!",
log(
"WARNING: macOS Seatbelt sandbox requested but 'sandbox-exec' was not found in PATH. " +
"With `CODEX_UNSAFE_ALLOW_NO_SANDBOX` enabled, continuing without sandbox.",
); );
return SandboxType.NONE;
} }
} else if (await isInLinux()) { } else if (CODEX_UNSAFE_ALLOW_NO_SANDBOX) {
return SandboxType.NONE; // Allow running without a sandbox if the user has explicitly marked the
} else if (process.platform === "win32") { // environment as already being sufficiently locked-down.
// 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.",
);
return SandboxType.NONE; 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!"); throw new Error("Sandbox was mandated, but no sandbox is available!");
} else { } else {
return SandboxType.NONE; return SandboxType.NONE;

View File

@@ -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_ORGANIZATION = process.env["OPENAI_ORGANIZATION"] || "";
export const OPENAI_PROJECT = process.env["OPENAI_PROJECT"] || ""; 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( export const CODEX_UNSAFE_ALLOW_NO_SANDBOX = Boolean(
process.env["CODEX_UNSAFE_ALLOW_NO_SANDBOX"] || "", process.env["CODEX_UNSAFE_ALLOW_NO_SANDBOX"] || "",
); );