Skip to content

[Bug]: ocx claude --dangerously-skip-permissions aborts as root (uid==0) because IS_SANDBOX is never set #1688

Closed

Description

@rrmlima

Summary

ocx claude --dangerously-skip-permissions aborts immediately when running as root (uid == 0), which is the default on most VPS deployments. Claude Code 2.1.205+ embeds a safety guard that exits with --dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons unless it believes it runs inside a sandbox (IS_SANDBOX=1) or a bubblewrap wrapper is present. The opencodex proxy launches the session, so it should mark the environment accordingly.

Reproduction

# As root (typical VPS):
ocx claude --dangerously-skip-permissions --print "ok"

Expected: the Claude session starts and prints ok.
Actual: immediate fatal exit with the root-guard message.

Logs and screenshots

--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons

Area

CLI

Version

2.14.2 (also on current main — IS_SANDBOX does not appear anywhere in src/)

OS

Ubuntu 24.04 (root user)

Config shape

N/A (CLI invocation only)

Root cause

src/cli/claude.ts buildClaudeEnv does not set IS_SANDBOX. The guard is in the official Claude Code binary, not in opencodex — but since the proxy owns the launched session (a controlled, proxy-launched environment), it must signal IS_SANDBOX=1 so the binary allows the bypass. Without it, every root-hosted deployment of ocx claude with permissions bypass is unusable.

Proposed fix

In buildClaudeEnv, before return env;:

   for (const [name, value] of Object.entries(effectiveModelEnv(config.claudeCode, contextWindows, auto))) {
     setDefault(name, value);
   }
+  // Root bypass guard (Claude Code 2.1.205+): the CLI refuses --dangerously-skip-permissions
+  // when uid==0 unless it believes it runs inside a sandbox. Servers are commonly root, so
+  // treat the proxy-launched session as one — opt out by exporting IS_SANDBOX=0 yourself.
+  if (typeof process.getuid === "function" && process.getuid() === 0) {
+    setDefault("IS_SANDBOX", "1");
+  }
   return env;

setDefault semantics already guarantee the user can override (IS_SANDBOX=0 exported by the user wins), so this is an opt-out-by-env design, not a forced change.

Alternatives considered

  • Document "run as non-root": impractical on VPS setups where the service owns root files, and Claude Code already provides the sandbox escape hatch (IS_SANDBOX) for exactly this case.
  • Require CLAUDE_CODE_BUBBLEWRAP: heavier, not portable; IS_SANDBOX is the documented, minimal signal.

Additional context

The same pattern is used by other proxy-launcher tools: the session is a controlled environment created by the parent process, so marking it as sandboxed for the child's safety guard is accurate and safe. The user opt-out (IS_SANDBOX=0) preserves the strict behavior for anyone who wants it.

Test matrix:

  • root + --dangerously-skip-permissions → session starts (200 / prints)
  • root + exported IS_SANDBOX=0 → original guard behavior preserved (user opted out)
  • non-root → unchanged (no IS_SANDBOX injected)

Activity

  1. added
    bugSomething isn't working
    cliCLI, config inject, packaging flags
    on Aug 14, 2026
  2. coderabbitai commented on Aug 14, 2026

    @coderabbitai
    Contributor
    🔗 Related PRs

    #573 - fix(server): treat forwarded loopback ports as loopback [merged]
    #936 - fix: harden credential and runtime trust boundaries (rebase of #916) [merged]
    #1358 - fix(claude): contain main credentials to canonical forward routes [merged]
    #1360 - fix(claude): bind proxy admission tokens to local destinations [merged]
    #1441 - fix(codex): stop recursive dynamic-launcher shims [open]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. Wibias commented on Aug 14, 2026

    @Wibias
    Contributor

    The workflow problem here is real, but #1701 is being closed because uid == 0 is not sandbox evidence. OpenCodex launching Claude as a child process does not itself create an OS security boundary, so automatically setting IS_SANDBOX=1 would claim isolation that does not exist and can suppress Claude Code's root guard while --dangerously-skip-permissions is active.

    Preferred solution: create a real sandboxed launch path

    If OpenCodex wants to support ocx claude --dangerously-skip-permissions as root, the clean solution is to make the sandbox claim true rather than bypass the check.

    Conceptually:

    root launch
      -> establish real OS-level sandbox/isolation
      -> verify sandbox setup succeeded
      -> set IS_SANDBOX=1 for the child
      -> launch Claude Code
    

    On Linux this should use an actual isolation primitive suitable for the project, for example a bubblewrap-based launch path or another reviewed sandbox mechanism. The exact implementation needs a threat-modelled contract for filesystem access, process access, network access, required mounts, working directory access, and any capabilities that must remain available for normal Claude Code operation.

    Important requirements:

    • Never derive IS_SANDBOX=1 from UID alone.
    • Set it only after the isolation mechanism has positively succeeded.
    • If sandbox creation or verification fails, fail closed and keep Claude Code's normal root protection intact. Do not silently fall back to an unsandboxed root launch with the flag set.
    • Preserve explicit user-provided sandbox-related environment where appropriate, but do not manufacture a sandbox claim merely because OpenCodex owns the launcher.
    • Add integration coverage that proves the child is actually launched through the isolation path, not only a unit test that checks the environment variable.
    • Keep non-root ocx claude behaviour unchanged.

    A smaller explicit unsafe opt-in could technically work as an escape hatch, but it still intentionally defeats an upstream safety guard. A real sandboxed launcher is the preferred long-term fix because it solves the original VPS/root workflow without weakening the meaning of IS_SANDBOX.

  4. added
    needs-infoWaiting on reporter for a concrete spec or reproduction
    on Aug 15, 2026
  5. yzxcj797 commented on Aug 18, 2026

    @yzxcj797
    Contributor

    Implementing with one deliberate deviation from the proposed diff: IS_SANDBOX=1 is set only when the user explicitly passed --dangerously-skip-permissions (detected in cmdClaude and threaded through ClaudeEnvDeps), not for every launch. Rationale: the variable exists to relax a vendor safety guard, and marking every proxy-owned session as sandboxed could silence other guard behaviors in future Claude Code versions; an ordinary ocx claude launch keeps the vendor's default posture. An explicit user export still wins (setDefault semantics).

    Tests cover the bypass launch setting it, the ordinary launch not setting it, and the user-export-wins case.

  6. lidge-jun commented on Aug 19, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 46 / 80

    루트(uid 0)에서 ocx claude --dangerously-skip-permissions를 쓰면 Claude Code가 바로 끝나는 버그입니다. 2.1.205+는 샌드박스 표시가 없으면 루트에서 이 플래그를 거부합니다. VPS에서 루트로 돌리는 경우가 많아, 프록시가 띄운 세션이 시작조차 못 합니다. needs-info가 붙어 있습니다.

    현재 dev의 src/cli/claude.ts buildClaudeEnv()는 ANTHROPIC_BASE_URL, 호스트 인증, 컨텍스트/모델 슬롯을 setDefault로 넣고 242행에서 env를 반환합니다. IS_SANDBOX는 src 안에 없습니다. setDefault는 사용자가 이미 넣은 값을 덮어쓰지 않으므로, 이슈가 말한 옵트아웃 설계와는 맞습니다.

    원하는 결과는 루트 VPS에서도 세션이 시작되는 것입니다. 다만 IS_SANDBOX=1은 Claude Code의 루트 가드를 우회하는 신호입니다. 프록시가 띄운 통제된 환경이라는 주장과, 실제 격리가 없다는 점이 같이 있습니다. 그래서 기본 주입을 넣을지는 보안 판단이 필요합니다. 사용자가 IS_SANDBOX=0을 내보내면 가드가 남는다는 점은 코드의 setDefault와 맞습니다.

    해결방안: 루트일 때만 setDefault로 IS_SANDBOX=1을 넣는 패치는 위치가 분명합니다. 비루트는 그대로 두고, 테스트는 루트+플래그, 루트+IS_SANDBOX=0, 비루트를 나누면 됩니다. 기본 주입을 거부한다면 문서에 비루트 실행 또는 사용자 환경 변수를 적는 대안이 있습니다. 공식 Claude Code 가드 계약이 이슈와 같은지는 needs-info로 남아 있습니다.

    이 댓글은 grok-bot이 작성했습니다

  7. lidge-jun commented on Aug 19, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 46 / 80 · 유지

    VPS에서 root로 ocx claude --dangerously-skip-permissions 하면 Claude 2.1.205+가 바로 죽음. buildClaudeEnv가 IS_SANDBOX를 안 넣어서임. 증상은 맞음.

    다만 root일 때 기본으로 IS_SANDBOX=1 넣는 패치는 반대임. 그 가드는 보안 이유로 있는 건데, ocx가 기본으로 속이면 skip-permissions가 루트 전체 파일에 열림. setDefault로 IS_SANDBOX=0이 이겨도, 기본값이 우회면 실수하기 쉬움.

    열어 둠. 고칠 거면 명시 플래그/환경변수로만. 문서에 root 비추. 자동 주입 PR은 안 받음.

    해결방안: --allow-root-skip-permissions 같은 opt-in이거나 사용자가 IS_SANDBOX=1을 직접 넣을 때만. 기본은 지금처럼 가드 유지. 가능하면 non-root로 돌리게.

    이 댓글은 grok-bot이 작성했습니다

  8. lidge-jun commented on Aug 25, 2026

    @lidge-jun
    Owner

    Fixed on dev by #2631 (squash 848a66d15).

    Your diagnosis was exact — Claude Code 2.1.205+ exits as root unless it believes it is sandboxed, and OpenCodex launches the session so it is the layer that can mark the environment.

    I did not set IS_SANDBOX=1 unconditionally, because that would tell Claude Code a safety guard is satisfied when it is not. The bypass requires both conditions: you passed --dangerously-skip-permissions explicitly (so you have already accepted that risk) and uid == 0. An ordinary launch is untouched, and an explicit IS_SANDBOX=0 of your own still wins — user values are never overwritten.

    The launch also says what happened, in both directions:

    ⚠ 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.
    

    A silent safety-guard bypass would have been the wrong shape even though it fixes the symptom — you should be able to tell from the output that no real sandbox exists.

    No bubblewrap wrapper was added. That is a Linux-only isolation feature with its own threat model, not something to bundle into a launcher flag fix; if you want real isolation on a VPS rather than a guard bypass, that is worth its own issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcliCLI, config inject, packaging flagsneeds-infoWaiting on reporter for a concrete spec or reproduction

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions