はじめに
Codex CLI に --approve-for-me というフラグがあります。ヘルプの説明は「Route approval requests through automatic review using the workspace-write sandbox」で、承認要求を workspace-write サンドボックス上の自動レビューに回す、と読めます。
承認プロンプトを消せるのはありがたいのですが、そのとき実際に許される操作の範囲がどこまでなのかは、この一文からは分かりません。workspace-write の「workspace」が作業ディレクトリだけを指すのか、それとももう少し広いのか。ここを取り違えたまま CI に載せると、リポジトリの外にあるファイルが黙って書き換わります。
対象読者は、Codex CLI を CI や自動化パイプラインで回していて、承認プロンプトを減らしたいと考えている開発者です。
この記事では codex sandbox サブコマンドを使い、モデル呼び出しなしでサンドボックスの実効境界を 5 パターン測りました。使用したのは codex-cli 0.147.0(npm パッケージ @openai/codex の最新版)です。
TL;DR
-
codex sandboxの既定モードは read-only で、作業ディレクトリへの書き込みもRead-only file systemで失敗しました - workspace-write では作業ディレクトリだけでなく
/tmpへの書き込みも通りました - 同じ workspace-write でも、作業ディレクトリ内の
.git配下への書き込みは拒否されました - ネットワークは read-only と workspace-write の両方で遮断され、danger-full-access のときだけ HTTP 200 が返りました
-
--approve-for-meは--sandboxと排他で、同じ workspace-write を明示指定しても引数エラーになりました
検証環境
| 項目 | 値 |
|---|---|
| Codex CLI | codex-cli 0.147.0(npm install @openai/codex@0.147.0) |
| 実行環境 | Linux x64 / Node.js 22 |
| 比較対象 | Claude Code 2.1.229 |
| 検証手段 |
codex sandbox サブコマンド(モデル呼び出し・ログイン不要) |
@openai/codex の最新版は npm view @openai/codex version で 0.147.0 でした。プラットフォーム別に 0.147.0-linux-x64 などの dist-tag も配信されています。
codex sandbox は認証なしでポリシーを試せる
codex --help を見ると、サブコマンドに sandbox があります。
sandbox Run commands within a Codex-provided sandbox
これはモデルに指示を出すのではなく、渡したコマンドを Codex のサンドボックス下でそのまま実行するものです。OpenAI API の認証なしで動くため、「エージェントが許可された操作」だけを切り出して確かめられます。
サンドボックスのモードは -c sandbox_mode=<mode> で切り替えます。ヘルプに載っている値は 3 つです。
-s, --sandbox <SANDBOX_MODE>
[possible values: read-only, workspace-write, danger-full-access]
公式ドキュメントの説明もこの 3 段階で、workspace-write は「The agent can read files, edit within the workspace, and run routine local commands inside that boundary.」と書かれています1。boundary の実体がどこかは書かれていないため、実際に叩いて確かめます。
実測1: 既定モードは read-only
まずモード指定なしで、作業ディレクトリにファイルを作ろうとしました。
codex sandbox -- sh -c 'echo hi > a.txt && echo WROTE_OK'
sh: 1: cannot create a.txt: Read-only file system
-c sandbox_mode=read-only を明示した場合も同じ結果でした。モード指定を忘れた codex sandbox は最も安全側に倒れる、という挙動です。
実測2: workspace-write が書けた範囲
-c sandbox_mode=workspace-write に切り替え、書き込み先を変えて 4 通り試しました。作業ディレクトリは /home/user/sbtest2/work(git init 済み)です。
codex sandbox -c sandbox_mode=workspace-write -- sh -c 'echo hi > in.txt && echo WROTE_OK'
codex sandbox -c sandbox_mode=workspace-write -- sh -c 'echo hi > ../parent.txt && echo WROTE_OK'
codex sandbox -c sandbox_mode=workspace-write -- sh -c 'echo hi > /tmp/ws-tmp-probe.txt && echo WROTE_OK'
codex sandbox -c sandbox_mode=workspace-write -- sh -c 'echo hi > .git/probe.txt && echo WROTE_OK'
| 書き込み先 | 結果 | 終了コード |
|---|---|---|
作業ディレクトリ直下 in.txt
|
WROTE_OK |
0 |
親ディレクトリ ../parent.txt
|
cannot create ../parent.txt: Read-only file system |
2 |
/tmp/ws-tmp-probe.txt |
WROTE_OK |
0 |
.git/probe.txt |
cannot create .git/probe.txt: Read-only file system |
2 |
親ディレクトリが拒否されるのは想像どおりでした。想像と違ったのは残り 2 つです。
/tmp は作業ディレクトリの外なのに書けました。workspace-write は「作業ディレクトリだけ」ではなく、一時ディレクトリを含む複数の書き込み可能ルートを持つモードだということです。逆に .git は作業ディレクトリの内側なのに拒否されました。エージェントが履歴やフックを直接書き換えないよう、workspace の内部に例外が切られています。
なお筆者が最初に検証したディレクトリは /tmp 配下にあり、そこでは「親ディレクトリへの書き込み」も成功していました。作業ディレクトリを /home/user 配下に移して測り直したところ拒否に変わったため、あの成功は workspace の緩さではなく /tmp が書けることの副作用でした。サンドボックスの検証では、作業ディレクトリ自体を /tmp の外へ置く必要があります。
実測3: ネットワークはモードで切り替わる
同じコマンドをモード違いで 3 回実行しました。
codex sandbox -c sandbox_mode=read-only -- sh -c 'curl -sS -o /dev/null -w "%{http_code}" https://example.com'
codex sandbox -c sandbox_mode=workspace-write -- sh -c 'curl -sS -o /dev/null -w "%{http_code}" https://example.com'
codex sandbox -c sandbox_mode=danger-full-access -- sh -c 'curl -sS -o /dev/null -w "%{http_code}" https://example.com'
| モード | 結果 |
|---|---|
| read-only |
curl: (7) Failed to connect / 終了コード 7 |
| workspace-write |
curl: (7) Failed to connect / 終了コード 7 |
| danger-full-access |
200 / 終了コード 0 |
danger-full-access でだけ 200 が返ったことで、接続失敗が実行環境側の問題ではなくサンドボックスによる遮断だと確認できました。ファイル書き込みが許される workspace-write でも、通信は既定で閉じています。
実測4: 穴は config キーで塞げる(開けられる)
/tmp が書けるのが困る場面はあります。ビルドキャッシュや他プロセスの一時ファイルを踏ませたくない場合です。-c で設定を上書きして挙動が変わるか試しました。
# /tmp を書き込み対象から外す
codex sandbox -c sandbox_mode=workspace-write -c sandbox_workspace_write.exclude_slash_tmp=true \
-- sh -c 'echo hi > /tmp/ws-tmp-probe2.txt && echo WROTE_OK'
# workspace-write のままネットワークを開ける
codex sandbox -c sandbox_mode=workspace-write -c sandbox_workspace_write.network_access=true \
-- sh -c 'curl -sS -o /dev/null -w "%{http_code}" https://example.com'
# 書き込み可能ルートを追加する
codex sandbox -c sandbox_mode=workspace-write -c 'sandbox_workspace_write.writable_roots=["/home/user/sbtest2"]' \
-- sh -c 'echo hi > ../parent2.txt && echo WROTE_OK'
| 設定 | 実測結果 |
|---|---|
exclude_slash_tmp=true |
/tmp への書き込みが Read-only file system に変わった |
network_access=true |
curl が 200 を返した |
writable_roots=["/home/user/sbtest2"] |
親ディレクトリへの書き込みが通った |
3 つとも期待どおり効きました。これらのキーは OpenAI の Config Reference に掲載されており、exclude_slash_tmp は「Exclude /tmp from writable roots in workspace-write mode.」、network_access は「Allow outbound network access inside the workspace-write sandbox.」と説明されています2。
注目したいのは、/tmp が既定で書けるという事実そのものは、この「除外できます」というキーの説明から逆算しないと分からない点です。モードを説明するページには boundary の中身が書かれていません。挙動を前提に CI を組むなら、キー一覧を読むだけで終わらせず codex sandbox で 1 コマンド実測しておくと確実です。
実測5: --approve-for-me は --sandbox と併用できない
最後に --approve-for-me 自体の受理条件を確かめました。
codex exec --approve-for-me -s danger-full-access "echo hi"
error: the argument '--approve-for-me' cannot be used with '--sandbox <SANDBOX_MODE>'
危険側の danger-full-access を弾くだけなら納得ですが、-s workspace-write を指定しても同じエラーになりました。ヘルプの説明どおり workspace-write を使うフラグなのに、同じ値を明示すると受理されません。フラグ自体がサンドボックスモードを内部で確定させるため、外から指定する余地を残していないということです。
もう 1 つ、承認ポリシーを選ぶ -a, --ask-for-approval はフラグの置き場所で結果が変わりました。
codex exec -a never "echo hi" # → error: unexpected argument '-a' found
codex -a never exec --help # → exec のヘルプが表示される(受理される)
codex exec の後ろに置くと弾かれますが、サブコマンドの前に置けば受理されます。-a はグローバルフラグで、exec 配下のオプション表には載っていないためです。ヘルプだけを見て「非対話実行では承認ポリシーを選べない」と判断すると、使える指定を取りこぼします。
なお -a と --approve-for-me も排他でした。
error: the argument '--ask-for-approval <APPROVAL_POLICY>' cannot be used with '--approve-for-me'
--approve-for-me はサンドボックスモードと承認ポリシーの両方を内部で確定させるフラグだと分かります。
Claude Code の permission-mode と比べる
同じマシンに入っている Claude Code 2.1.229 の --help を見ると、権限モードは 6 つでした。
--permission-mode <mode> Permission mode to use for the session
(choices: "acceptEdits", "auto",
"bypassPermissions", "manual", "dontAsk", "plan")
公式ドキュメントでは manual は default の別名として扱われていますが、実機のヘルプ出力は上記のとおり manual を表示しました。
| 観点 | Codex CLI 0.147.0 | Claude Code 2.1.229 |
|---|---|---|
| 非対話での選択肢 |
--approve-for-me、またはサブコマンド前に置いた -a
|
--permission-mode に 6 値 |
| 書き込み境界の指定 |
-s の 3 モード + writable_roots 等の config |
--add-dir や settings の permissions |
| 境界だけを単体検証する手段 |
codex sandbox サブコマンド |
専用サブコマンドは無し |
比較して価値が高いと感じたのは codex sandbox の存在です。Claude Code 側で「このモードなら何が実行できるか」を確かめるには、実際にセッションを開いてモデルにコマンドを提案させる必要があります。Codex CLI はサンドボックスだけを切り離して叩けるため、権限設計をテストコードに落とせます。
著者視点の発見ポイント
3 点あります。
1 点目は、workspace-write という名前が実効境界を正確に表していないことです。作業ディレクトリの内側でも .git は書けず、外側でも /tmp は書けます。名前ではなく実測で境界を確認する前提に立ったほうがよいと考えます。
2 点目は、/tmp が書けることの評価が環境で変わる点です。ローカル開発では一時ファイルが作れないほうが不便ですが、共有ランナーでは他ジョブの成果物と同じ場所に書けることになります。CI に載せるなら exclude_slash_tmp=true を既定にしておくほうが、事故の説明がしやすくなります。
3 点目は、--approve-for-me と --sandbox の排他です。「自動承認を有効にしつつ書き込み範囲を明示する」という直感的な組み合わせが引数エラーで止まるため、CI のコマンドラインを組むときに気づけます。実行時に静かに緩むより扱いやすい設計だと感じました。
おわりに
codex sandbox を使うと、モデルを動かさずにサンドボックスの実効境界を測れます。今回の 5 パターンで分かったのは、workspace-write が作業ディレクトリと /tmp を書けて .git を書けないこと、ネットワークは既定で閉じていること、そして --approve-for-me がサンドボックス指定と排他であることでした。
自動承認を CI で使うなら、まず codex sandbox で自分のワークスペース構成に対して同じ 5 パターンを流し、境界が想定どおりか確かめてから組み込むことをおすすめします。
関連記事
- Copilot CLIのサンドボックス、Linuxはbwrapが無いと動かない
- Kimi Code CLIをOpenAIキーで動かしたら、標準の-pでも自動修正が走った
- Claude Code archiveプラグイン、sha256改ざん検知を実機で確認
-
Codex Security ドキュメント(サンドボックスモードと
sandbox_workspace_write.writable_rootsの記載): https://learn.chatgpt.com/docs/sandboxing ↩ -
Codex Config Reference(
sandbox_workspace_write.exclude_slash_tmp/network_access/writable_roots/exclude_tmpdir_env_varの記載): https://learn.chatgpt.com/docs/config-file/config-reference ↩