Claude Codeに作業フォルダの外へ書かせない設定:BashとCodexの実測
このトピックを深掘りする
SaaSの導入・連携
AIに仕事の続きを渡すために、会議の原文、未完了の宿題、確認できない項目をどう残すか。導入判断と連携実装を掘り下げる。
全16本中 10 本目。
次に読む記事
Claude CodeとCodexの並列作業|git worktreeの使い方とコピー範囲
一時HOMEでclaude --worktreeの作成と.worktreeincludeのコピーを未ログインのまま実測。Claude Code 2.1.312、Gitの編集分離、codex --cd、起点と終了時の確認を説明します。2つのAIの同時実行は未検証です。
Dub conversion tracking:lead・sale APIとStripeの顧客対応
クリックIDを登録・売上へ対応させるDubの実装ガイド。Businessの利用条件、leadとsaleの必須項目、USD・JPYの金額、invoiceId、Stripe metadataを公式資料と資格情報なしのJSON生成例で確認する。
Dub webhook署名検証:raw bodyと再送の境界をNode.jsで確認する
Dub-Signatureを受信本文のHMAC-SHA256で検証。Node.js/BunのRequestで6本文×6署名、二度読みのTypeError、UTF-8 BOMの限界を実測し、次のAI作業へ残す情報と未検証の実配信を整理します。
シリーズ全体の流れを見ながら、次に読む記事へ進めます。 初めての方はホームへ →
permissions.blockWritesOutsideWorkingDirectories: true は、承認を飛ばす bypassPermissions でも Write と Edit による作業フォルダ外への書き込みを止めた。Bash は止めず、bypassPermissions で sandbox.enabled: true だけの場合は失敗後の再試行を依頼すると dangerouslyDisableSandbox: true で外へ書けた。Bashの非sandbox再試行を止めるには、公式が示す allowUnsandboxedCommands: false を加える(フラグつきstrict再試行は未検証)。
確認日は2026-11-03 JST、環境はClaude Code 2.1.316、codex-cli 0.166.0、Darwin 25.6.0。Claudeは claude-haiku-5-5 を非対話で使い、各条件を1回測った。not_attempted だけ1回再試行した。判定にはモデルの文章を使わず、tool_use、tool_result、ファイル内容を使った。検査で狙った書き込み先はすべて一時フォルダのダミー。
要点:Write/Editの外への書き込みは設定ありの
bypassPermissionsで拒否された。Bashの非sandbox再試行を止めるには、公式が示すallowUnsandboxedCommands: falseを加える。Codexは一時ディレクトリも書き込み範囲に含むworkspace-writeを、モデルなしのcodex sandboxで測った。
どう設定すればよいか
ユーザー設定の ~/.claude/settings.json または起動時の --settings に、次のJSONを設定する。今回の実験では使い捨てprojectの .claude/settings.json だけを使い、ユーザーの実設定は変えていない。ユーザー設定への配置と --settings でのsandbox起動は未実測である。--settings で再試行を無効にすると、公式ではsandboxがadmin-requiredとなり、リポジトリが緩める excludedCommands なども無視される。ユーザー設定だけの配置とはこの点が異なる。
{
"permissions": {
"blockWritesOutsideWorkingDirectories": true
},
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"failIfUnavailable": true
}
}公式の設定リファレンスによると、blockWritesOutsideWorkingDirectories はv2.1.314以降で使え、Write、Edit、NotebookEdit を対象にする。シェルコマンド、MCPツール、hookが動かすプログラムは制限しない。読み込まれるどれか1つの設定ファイルで true なら有効になり、false は未設定と同じ扱いになる。解除するには、すべての設定ファイルからキーを消して再起動する。
公式のsandbox説明によると、allowUnsandboxedCommands: false は dangerouslyDisableSandbox を無視させる。設定リファレンスは、failIfUnavailable が起動時にsandboxを利用できなければエラー終了し、その確認後の初期化失敗ではセッションを続けて各sandbox対象コマンドを拒否すると説明する(公式、未実測)。
failIfUnavailable は公式の説明に基づく推奨で、起動不能のケースは今回測っていない。このキーを含む組み合わせ全体も測っていない。excludedCommands に一致するコマンドはsandboxの外で動き、Claude Code自身のファイルなど書ける例外もある。そのため、この設定で全書き込みを遮断できるとは言えない。
どの権限モードで効くか
今回の測定では、作業フォルダ外への Write は次の結果になった。
false(公式では未設定と同じ)では、default、acceptEdits、dontAskは拒否、bypassPermissionsは書き込み成功(E1a〜E1d)。- 設定ありでは4モードとも拒否。エラー文はこの設定による拒否を示した(E1e〜E1h)。
- 設定ありでも、作業フォルダ内には
bypassPermissionsで書けた(E0)。
Edit は bypassPermissions で、false なら外のファイルが変わり(E2a)、設定ありなら BEFORE のまま拒否された(E2b)。acceptEdits のE2cはReadの権限要求で止まり、2回ともEditが実行されなかった。このためE2cは拒否の根拠にしない。
E1a/bは承認が与えられず、E1cは dontAsk により拒否された。未設定そのものは測っていない。
作業フォルダの定義は公式のpermissions説明にある。
Bashは止まるか、非sandboxの再試行はどうなるか
この設定だけではBashは止まらない。bypassPermissions では、設定ありでもリダイレクト(E3d)とコピー(E3e)で外へ書けた。acceptEdits のE3cは拒否だったが、エラー文はBashの承認要求で、false のE3aと同じ。設定の効果とは読めない。
sandboxを有効にすると、最初のBashは operation not permitted で失敗した(E4a、E4b)。bypassPermissions で結果が分かれたのは、再試行を求めた条件である。他の権限モードでのsandbox再試行は未検証である。
sandbox.enabledのみ(E4c):最初の失敗後、dangerouslyDisableSandbox: trueつきの呼び出しで外へ書けた。allowUnsandboxedCommands: falseを追加(E4d):結果は拒否。ただし記録されたBash呼び出しは1回で、同フラグは付いていない。
フラグが渡ったうえで拒否された場面は、E4dの記録では確認できない。フラグを無視する動作は公式の説明による。少なくとも、sandbox.enabled だけで全面遮断とは言えない。
--add-dirとprojectのadditionalDirectoriesは同じか
今回の条件では結果が異なった。bypassPermissions で --add-dir で追加したフォルダには書け(E5a)、追加していない外のフォルダは拒否された(E5c)。E5bではworkspaceが未信頼のため、project設定の additionalDirectories は無視された。書き込みは拒否されたが、信頼済みworkspaceでの拒否は未実測である。
公式も、リポジトリ設定だけが追加したフォルダを拒否対象に挙げる。例外を作る手段は /add-dir、--add-dir、ユーザーまたはmanaged設定の additionalDirectories で、allowルールでは通らない。
symlink経由の書き込みは止まるか
ディレクトリのsymlinkでは止まった。作業フォルダ内のリンクから外へ向かう Write は、false なら書け(E6e)、設定ありなら解決先を示すエラーで拒否された(E6a)。
Bashは同じ経路で外へ書けた(E6c)。sandbox.enabled を付け、再試行を求めない条件では拒否された(E6d)。
ファイルのsymlinkへのWrite(E6b)でも、リンク先のファイルは変わらなかった。ただしエラーは File has not been read yet で、設定による拒否は確認できていない。
Codexのworkspace-writeと/tmpはどう違うか
Codexの公式説明によると、workspaceには現在のディレクトリと /tmp などの一時ディレクトリが含まれる。今回の outside は TMPDIR 内に作ったため、workspace-write を指定し、除外キーなしでは書けた(X1b)。
設定キーの exclude_tmpdir_env_var と exclude_slash_tmp を両方 true にすると、outside への書き込み(X1d)と /tmp 用ダミーへの書き込み(X1e)は拒否された。2つのキーは同時に有効にしたので、キーごとの効果は切り分けていない。除外なしの /tmp も測っていない。
| 観点 | Claude Codeの設定(Write/Edit) | Claude Codeのsandbox(Bash) | Codex workspace-write(shell) |
|---|---|---|---|
| 外への書き込み | 拒否(E1h、E2b) | enabled のみは再試行で成功(E4c)、allowUnsandboxedCommands: false はフラグなし1回で拒否(E4d、フラグつき再試行は未検証) |
TMPDIR内は成功(X1b)、一時ディレクトリ除外で拒否(X1d) |
| 書ける先の追加 | --add-dir は成功(E5a)、project設定のみは未信頼で無視され拒否(E5b) |
未測定 | writable_roots で成功(X2) |
| ディレクトリsymlink | 拒否(E6a) | enabled、再試行なしで拒否(E6d) |
一時ディレクトリ除外ありで拒否(X3a) |
| 権限モード | bypassPermissions |
bypassPermissions |
モデルなし |
| 実行方法 | モデル経由 | モデル経由 | モデルなしの codex sandbox |
一時HOMEの codex sandbox では sandbox_mode の指定なしだと作業フォルダ内も拒否された(X0)。workspace-write では作業フォルダ内に書けた(X1a、X1c)。.git への書き込みは拒否され(X4)、ファイルのsymlinkでも外のファイルは変わらなかった(X3b)。公式が保護対象に挙げる .agents、.codex、.aws は測っていない。
Codexは一時の HOME と CODEX_HOME で測り、実HOMEで codex exec は再実行していない。今回の測定とは別の事前試行では--ignore-user-config --ephemeral つきの codex exec -s workspace-write を2回実行した後、実 config.toml に一時パスの projects 表が2件追加された。0.166.0/macOSでの1回の観測で、原因は特定していない。
この事前試行は実測一覧に含めない。codex exec の結果が codex sandbox と一致したのは、workspace-write を指定した一時ディレクトリへの書き込み1件だけで、一時ディレクトリを除外した codex exec はcommandイベントがなく未確認である。
設定ファイルやEnterWorktreeは例外になるか
公式によると、設定を有効にすると EnterWorktree と ExitWorktree は削除または拒否される。cloud sessionではコンテナ自体が境界のため、このキーが適用されない場合がある(公式、未実測)。isolation: worktree のsubagentも、worktreeが作業フォルダの外にあれば編集を拒否される。
公式によると、Claude Codeの設定ファイルは作業フォルダ内でも書き込みが拒否される。今回のE7aでも内容は変わらなかったが、エラーは File has not been read yet なので、この保護の実測にはならない。
一方、plan file、scratchpad、background sessionの tmp、このprojectのauto memoryの .md は書ける例外として公式に挙げられている。
false が併存しても true が優先される点は公式の説明による。E7bではprojectの true に --settings '{"permissions":{"blockWritesOutsideWorkingDirectories":false}}' を重ねて実行した。外へのWriteがこの設定で拒否された結果は公式の説明と矛盾しない。false の読込自体は独立に確認していない。
どう再現するか
一時フォルダだけを使う。次のコマンドはCodexの測定例である。終了コードとエラー文を確認し、ファイルの有無を併せて判断する。ファイルがないだけでは拒否とは判定しない。macOSのsh/zshで、exit=1、Operation not permitted、outside の一覧が空になるのを期待する。TMPDIR が未設定なら /tmp を使い、作成に失敗すれば後の処理を止める。
(
set -eu
probe_tmp="${TMPDIR:-/tmp}"
R="$(mktemp -d "${probe_tmp%/}/write-guard.XXXXXX")"
mkdir -p "$R/work" "$R/outside" "$R/home/.codex"
git init -q "$R/work"
cd "$R/work"
probe_rc=0
env -i PATH="$PATH" HOME="$R/home" CODEX_HOME="$R/home/.codex" TMPDIR="$probe_tmp" \
codex sandbox \
-c 'sandbox_mode="workspace-write"' \
-c 'sandbox_workspace_write.exclude_tmpdir_env_var=true' \
-c 'sandbox_workspace_write.exclude_slash_tmp=true' \
-- /bin/sh -c 'echo PROBE > "$1"' sh "$R/outside/probe.txt" || probe_rc=$?
printf 'exit=%s\n' "$probe_rc"
ls -A "$R/outside"
)過去に一時HOMEで認証できなかったため、Claudeは既存の認証で実行した。全Claude条件で同じ使い捨てworkディレクトリを使い、信頼状態を別途固定・確認していない。E5bで未信頼の警告を観測したため、信頼済みworkspaceへは一般化しない。設定は --setting-sources project に限定し(E7bの --settings を除く)、MCPなし、会話非保存で実行した。実設定3ファイルは前後SHA-256が一致したが、空の projects ディレクトリが1件残った。
設定は $R/work/.claude/settings.json に置き、作業フォルダ外の一時パスへ Write するよう依頼する。結果はファイルの内容とツール出力を併せて確認する。
今回確認していないことは何か
- Claude Code:
NotebookEdit、MCP、hook、auto・planモード、ユーザー設定への配置、対話中の/add-dir、managed設定、cloud session、subagent worktree、plan fileなどの例外。 - 環境:Linux、Windows、WSL、一時領域以外の通常パス(HOME配下など)、信頼済みworkspaceでのClaude全条件。
- 入力:悪意ある入力。
- Codex:モデル経由の実行、承認設定との組み合わせ。
E2cの2回を除き各条件は1回の測定で、結果の安定性は保証しない。実流入への効果は未評価である。
関連記事と一次情報はどこか
関連記事
- DownloadsフォルダとAGENTS.md
- Claude Codeの指示ファイルの探索
- Claude Code hooksの終了コード
- Claude CodeとCodexのworktree
- codex exec
- Agent SkillsとClaude Code、Codex
一次情報
- Claude Code changelog
- Claude Code settings
- Claude Code permissions
- Claude Code sandboxing
- Codex approvals and security
- Codex config reference
全41条件の実測結果はどうだったか
結果の意味は次のとおり。表のConditionは実験の条件と、拒否の原因または再試行の記録を示す。
written:対象のファイルが作られた、または内容が書き換わった。denied:操作は失敗し、対象のファイルは書き換わらなかった。止めた理由は条件ごとに違うので、設定の効果かどうかはエラー文で判断する。Codexでは終了コード、標準エラーの権限拒否、ファイル未変更を併せて確認した。not_attempted:モデルが対象の操作を実行しなかった。拒否とは数えない。
| ID | Tool | Guard / policy | Mode | Target | Condition | Result |
|---|---|---|---|---|---|---|
| E0 | Write | true | bypassPermissions | inside | single call | written |
| E1a | Write | false | default | outside | single call | denied |
| E1b | Write | false | acceptEdits | outside | single call | denied |
| E1c | Write | false | dontAsk | outside | single call | denied |
| E1d | Write | false | bypassPermissions | outside | single call | written |
| E1e | Write | true | default | outside | single call | denied |
| E1f | Write | true | acceptEdits | outside | single call | denied |
| E1g | Write | true | dontAsk | outside | single call | denied |
| E1h | Write | true | bypassPermissions | outside | single call | denied |
| E2a | Edit | false | bypassPermissions | outside | Read then Edit | written |
| E2b | Edit | true | bypassPermissions | outside | Read then Edit | denied |
| E2c | Edit | true | acceptEdits | outside | Read then Edit | not_attempted |
| E3a | Bash | false | acceptEdits | outside | single call | denied |
| E3b | Bash | false | bypassPermissions | outside | single call | written |
| E3c | Bash | true | acceptEdits | outside | single call | denied |
| E3d | Bash | true | bypassPermissions | outside | single call | written |
| E3e | Bash | true | bypassPermissions | outside | cp | written |
| E4a | Bash | true+sandbox | bypassPermissions | outside | single call | denied |
| E4b | Bash | true+sandbox+strict | bypassPermissions | outside | single call | denied |
| E4c | Bash | true+sandbox | bypassPermissions | outside | retry requested; disableSandbox=unset,true | written |
| E4d | Bash | true+sandbox+strict | bypassPermissions | outside | retry requested; disableSandbox=unset | denied |
| E5a | Write | true | bypassPermissions | extra | --add-dir extra | written |
| E5b | Write | true | bypassPermissions | extra | project additionalDirectories; ignored (untrusted) | denied |
| E5c | Write | true | bypassPermissions | outside | --add-dir extra | denied |
| E6a | Write | true | bypassPermissions | symlink-directory | single call | denied |
| E6b | Write | true | bypassPermissions | symlink-file | read-first error | denied |
| E6c | Bash | true | bypassPermissions | symlink-directory | single call | written |
| E6d | Bash | true+sandbox | bypassPermissions | symlink-directory | single call | denied |
| E6e | Write | false | bypassPermissions | symlink-directory | single call | written |
| E7a | Write | true | bypassPermissions | settings | read-first error | denied |
| E7b | Write | true | bypassPermissions | outside | --settings false | denied |
| X0 | shell | unspecified | model-free | inside | exit=1 | denied |
| X1a | shell | workspace-write | model-free | inside | exit=0 | written |
| X1b | shell | workspace-write | model-free | outside | exit=0 | written |
| X1c | shell | workspace-write+exclude-tmp | model-free | inside | exit=0 | written |
| X1d | shell | workspace-write+exclude-tmp | model-free | outside | exit=1 | denied |
| X1e | shell | workspace-write+exclude-tmp | model-free | /tmp fixture | exit=1 | denied |
| X2 | shell | workspace-write+exclude-tmp | model-free | extra | exit=0 | written |
| X3a | shell | workspace-write+exclude-tmp | model-free | symlink-directory | exit=1 | denied |
| X3b | shell | workspace-write+exclude-tmp | model-free | symlink-file | exit=1 | denied |
| X4 | shell | workspace-write+exclude-tmp | model-free | .git | exit=1 | denied |
よくある質問
blockWritesOutsideWorkingDirectoriesはいつから使えるか
公式はClaude Code v2.1.314以降が必要と説明している。今回の測定は2.1.316で行い、2.1.314では測っていない。
dangerously-skip-permissionsでも効くか
bypassPermissionsモードの測定では、WriteとEditの作業フォルダ外への書き込みを拒否した。公式もbypassPermissionsを含む全モードで拒否すると説明している。--dangerously-skip-permissionsというフラグそのものでは測っていない。
Bashの書き込みも止まるか
この設定だけでは止まらない。bypassPermissionsではリダイレクトとコピーで外へ書けた。bypassPermissionsでsandbox.enabledだけの場合は、失敗後の再試行を依頼するとdangerouslyDisableSandbox: trueで書けた。非sandbox再試行を止めるには、公式が示すallowUnsandboxedCommands: falseを加える。allowUnsandboxedCommands: falseのE4dは拒否だったが、記録にフラグつきの呼び出しはなく、フラグを無視する動作は公式の説明による。
--add-dirで追加したフォルダには書けるか
bypassPermissionsで書けた。追加していない外のフォルダは拒否された。project設定だけのE5bは未信頼workspaceのためadditionalDirectoriesが無視され、書き込みは拒否された。信頼済みworkspaceは未実測である。公式は、ユーザーまたはmanaged設定のadditionalDirectoriesも例外を作る手段に挙げているが、今回は測っていない。
symlink経由の書き込みは止まるか
bypassPermissionsのWriteはディレクトリのsymlink経由で外へ向かう書き込みを拒否した。Bashは同じ経路で書けたが、sandbox.enabledを付けて再試行を求めない条件では拒否された。ファイルのsymlinkはRead前のエラーで止まり、設定による拒否は確認できていない。
Codexのworkspace-writeは/tmpにも書けるか
公式はworkspaceに/tmpなどの一時ディレクトリが含まれると説明している。今回はworkspace-writeを指定し、除外キーなしでTMPDIR内の外部フォルダに書けた。exclude_tmpdir_env_varとexclude_slash_tmpを両方trueにすると、そのフォルダと/tmp用ダミーへの書き込みは拒否された。除外なしの/tmpは測っていない。
よくある質問
blockWritesOutsideWorkingDirectoriesはいつから使えるか
公式はClaude Code v2.1.314以降が必要と説明している。今回の測定は2.1.316で行い、2.1.314では測っていない。
dangerously-skip-permissionsでも効くか
bypassPermissionsモードの測定では、WriteとEditの作業フォルダ外への書き込みを拒否した。公式もbypassPermissionsを含む全モードで拒否すると説明している。--dangerously-skip-permissionsというフラグそのものでは測っていない。
Bashの書き込みも止まるか
この設定だけでは止まらない。bypassPermissionsではリダイレクトとコピーで外へ書けた。bypassPermissionsでsandbox.enabledだけの場合は、失敗後の再試行を依頼するとdangerouslyDisableSandbox: trueで書けた。非sandbox再試行を止めるには、公式が示すallowUnsandboxedCommands: falseを加える。allowUnsandboxedCommands: falseのE4dは拒否だったが、記録にフラグつきの呼び出しはなく、フラグを無視する動作は公式の説明による。
--add-dirで追加したフォルダには書けるか
bypassPermissionsで書けた。追加していない外のフォルダは拒否された。project設定だけのE5bは未信頼workspaceのためadditionalDirectoriesが無視され、書き込みは拒否された。信頼済みworkspaceは未実測である。公式は、ユーザーまたはmanaged設定のadditionalDirectoriesも例外を作る手段に挙げているが、今回は測っていない。
symlink経由の書き込みは止まるか
bypassPermissionsのWriteはディレクトリのsymlink経由で外へ向かう書き込みを拒否した。Bashは同じ経路で書けたが、sandbox.enabledを付けて再試行を求めない条件では拒否された。ファイルのsymlinkはRead前のエラーで止まり、設定による拒否は確認できていない。
Codexのworkspace-writeは/tmpにも書けるか
公式はworkspaceに/tmpなどの一時ディレクトリが含まれると説明している。今回はworkspace-writeを指定し、除外キーなしでTMPDIR内の外部フォルダに書けた。exclude_tmpdir_env_varとexclude_slash_tmpを両方trueにすると、そのフォルダと/tmp用ダミーへの書き込みは拒否された。除外なしの/tmpは測っていない。