From b46b596e5fc5f838d884f40a399d3d410f916115 Mon Sep 17 00:00:00 2001 From: Shuto Otaki <105141999+shutootaki@users.noreply.github.com> Date: Sat, 19 Apr 2025 14:42:19 +0900 Subject: [PATCH] fix: enable shell option for child process execution (#391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changes - Added a `requiresShell` function to detect when a command contains shell operators - In the `exec` function, enabled the `shell: true` option if shell operators are present ## Why This Is Necessary See the discussion in this issue comment: https://github.com/openai/codex/issues/320#issuecomment-2816528014 ## Code Explanation The `requiresShell` function parses the command arguments and checks for any shell‑specific operators. If it finds shell operators, it adds the `shell: true` option when running the command so that it’s executed through a shell interpreter. --- codex-cli/src/utils/agent/exec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/codex-cli/src/utils/agent/exec.ts b/codex-cli/src/utils/agent/exec.ts index 22b75c02..876e144a 100644 --- a/codex-cli/src/utils/agent/exec.ts +++ b/codex-cli/src/utils/agent/exec.ts @@ -1,5 +1,6 @@ import type { ExecInput, ExecResult } from "./sandbox/interface.js"; import type { SpawnOptions } from "child_process"; +import type { ParseEntry } from "shell-quote"; import { process_patch } from "./apply-patch.js"; import { SandboxType } from "./sandbox/interface.js"; @@ -8,9 +9,17 @@ import { exec as rawExec } from "./sandbox/raw-exec.js"; import { formatCommandForDisplay } from "../../format-command.js"; import fs from "fs"; import os from "os"; +import { parse } from "shell-quote"; const DEFAULT_TIMEOUT_MS = 10_000; // 10 seconds +function requiresShell(cmd: Array): boolean { + return cmd.some((arg) => { + const tokens = parse(arg) as Array; + return tokens.some((token) => typeof token === "object" && "op" in token); + }); +} + /** * This function should never return a rejected promise: errors should be * mapped to a non-zero exit code and the error message should be in stderr. @@ -33,6 +42,7 @@ export function exec( const opts: SpawnOptions = { timeout: timeoutInMillis || DEFAULT_TIMEOUT_MS, + ...(requiresShell(cmd) ? { shell: true } : {}), ...(workdir ? { cwd: workdir } : {}), }; // Merge default writable roots with any user-specified ones.