diff --git a/src/cli/claude.ts b/src/cli/claude.ts index ebb77d3359..07f9599f50 100644 --- a/src/cli/claude.ts +++ b/src/cli/claude.ts @@ -34,8 +34,6 @@ export type ClaudeEnvDeps = { authDetect?: Omit, "env" | "ownTokens">; /** Test seam; production uses the authenticated Node-launcher context. */ preBunAnthropicSlots?: readonly AnthropicParentEnvSlot[] | null; - /** Explicit unsafe opt-in from a root `--dangerously-skip-permissions` launch. */ - allowRootSkipPermissions?: boolean; }; function isClaudeLoopbackHostname(hostname: string): boolean { @@ -117,9 +115,6 @@ export function buildClaudeEnv( if (env[name] !== undefined && env[name] !== "") return; // user wins env[name] = value; }; - if (deps.allowRootSkipPermissions === true) { - setDefault("IS_SANDBOX", "1"); - } setDefault("ANTHROPIC_BASE_URL", `http://127.0.0.1:${port}`); const existingBaseUrl = env.ANTHROPIC_BASE_URL; if (existingBaseUrl) { @@ -306,22 +301,6 @@ export function claudeNotFoundHint( return platform === "win32" && code === 9009 && !signal ? CLAUDE_INSTALL_HINT : null; } -export function shouldAllowRootSkipPermissions( - args: readonly string[], - getuid: (() => number) | null | undefined = process.getuid, -): boolean { - return args.includes("--dangerously-skip-permissions") - && typeof getuid === "function" - && getuid() === 0; -} - -export function rootSkipPermissionsNotice(env: ClaudeLaunchEnv): string { - if (env.IS_SANDBOX === "1") { - return "⚠ Root --dangerously-skip-permissions requested: OpenCodex set IS_SANDBOX=1 to bypass Claude Code's root guard. OpenCodex did not create an OS sandbox; prefer running as a non-root user."; - } - return `⚠ Root --dangerously-skip-permissions requested: preserving user IS_SANDBOX=${env.IS_SANDBOX}; Claude Code's root guard remains in control.`; -} - export async function cmdClaude(args: string[]): Promise { const config = loadConfig(); if (config.claudeCode?.enabled === false) { @@ -334,11 +313,7 @@ export async function cmdClaude(args: string[]): Promise { return 1; } const contextWindows = await fetchClaudeContextWindows(config, port); - const allowRootSkipPermissions = shouldAllowRootSkipPermissions(args); - const env = buildClaudeEnv(config, port, process.env, contextWindows, { allowRootSkipPermissions }); - if (allowRootSkipPermissions) { - console.error(rootSkipPermissionsNotice(env)); - } + const env = buildClaudeEnv(config, port, process.env, contextWindows); // Pre-write the CLI's gateway-model cache (devlog 030): without a token the CLI // never refreshes it, so the picker would keep showing yesterday's aliases. try { diff --git a/tests/claude-cli.test.ts b/tests/claude-cli.test.ts index aa0950a9b8..ad7cb417af 100644 --- a/tests/claude-cli.test.ts +++ b/tests/claude-cli.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { buildClaudeEnv, claudeNotFoundHint, rootSkipPermissionsNotice, shouldAllowRootSkipPermissions } from "../src/cli/claude"; +import { buildClaudeEnv, claudeNotFoundHint } from "../src/cli/claude"; import { commandInvocation } from "../src/lib/win-exec"; import type { OcxConfig } from "../src/types"; @@ -26,38 +26,9 @@ const AUTH_PRESENT = { }; describe("ocx claude env assembly", () => { - test("root skip-permissions bypass requires both the explicit flag and uid 0", () => { - expect(shouldAllowRootSkipPermissions(["--dangerously-skip-permissions"], () => 0)).toBe(true); - expect(shouldAllowRootSkipPermissions([], () => 0)).toBe(false); - expect(shouldAllowRootSkipPermissions(["--dangerously-skip-permissions"], () => 1000)).toBe(false); - expect(shouldAllowRootSkipPermissions(["--dangerously-skip-permissions"], null)).toBe(false); - }); - - test("root skip-permissions opt-in marks only that launch as sandboxed", () => { - const bypass = buildClaudeEnv(cfg(), 10100, {}, {}, { - ...AUTH_PRESENT, - allowRootSkipPermissions: true, - }); - expect(bypass.IS_SANDBOX).toBe("1"); - - const ordinary = buildClaudeEnv(cfg(), 10100, {}, {}, AUTH_PRESENT); - expect(ordinary.IS_SANDBOX).toBeUndefined(); - }); - - test("an explicit user sandbox value wins over the root skip-permissions opt-in", () => { - const env = buildClaudeEnv(cfg(), 10100, { IS_SANDBOX: "0" }, {}, { - ...AUTH_PRESENT, - allowRootSkipPermissions: true, - }); - expect(env.IS_SANDBOX).toBe("0"); - expect(rootSkipPermissionsNotice(env)).toContain("preserving user IS_SANDBOX=0"); - expect(rootSkipPermissionsNotice(env)).toContain("root guard remains in control"); - }); - - test("the unsafe root bypass notice discloses that no OS sandbox was created", () => { - const notice = rootSkipPermissionsNotice({ IS_SANDBOX: "1" }); - expect(notice).toContain("set IS_SANDBOX=1"); - expect(notice).toContain("did not create an OS sandbox"); + test("does not assert a sandbox that OpenCodex did not create", () => { + expect(buildClaudeEnv(cfg(), 10100, {}, {}, AUTH_PRESENT).IS_SANDBOX).toBeUndefined(); + expect(buildClaudeEnv(cfg(), 10100, { IS_SANDBOX: "0" }, {}, AUTH_PRESENT).IS_SANDBOX).toBe("0"); }); test("injects base URL, discovery flag and model slots — NO auth token by default (subscription mode)", () => {