ブログ • Claude Code / 権限

Claude Code 2.1.314 に上げ、permissions.blockWritesOutsideWorkingDirectories を有効にしたとき、拒否された Write と Edit の path を JSON ログにどう書くか?

2026年10月30日 Anthropic は Claude Code 2.1.314 を出した。permissions.blockWritesOutsideWorkingDirectories を追加し true のとき Write、Edit、NotebookEdit は default、acceptEdits、bypassPermissions など任意 permission モードで workingDirectories 外 path を拒否する。

これは permission policy の allow や deny 规则とは別層である。後者はツール種別と対話承認、作業ディレクトリ外書き込みは実行前に path 前缀で硬く止める。監査 JSON は設定开关、セッション workingDirectories、tool と path、decision denied と reason outside_working_directories を残し write_path_gate と照合する。

allow と deny キューは Agent permission policy。Managed Agents の auto 評価文脈は Managed Agents auto 評価。セッション工作流 JSON は Claude Code workflow。本記事は 2.1.314 の作業ディレクトリ外書き込み拦截、ログフィールド形状、write_path_gate 分岐のみを書く。

先にこれだけ:2.1.314 が変えるのは作業ディレクトリ境界での書き込み硬挡と対応ログフィールドであり permission policy の allow や deny 表の書き換えではない。以下 changelog、settings JSON、セッション workingDirectories、Write Edit NotebookEdit 呼び出し、拒否ログと表、write_path_gate 源码、JSONNote での形状検証。

2.1.314 は作業ディレクトリ外の Write、Edit、NotebookEdit をどう一律に止めるか

変更は Claude Code changelog、タグは v2.1.314 release。以前版比で目立つのは permissions.blockWritesOutsideWorkingDirectories。有効化後は書き込み tool が bypassPermissions や acceptEdits でも /etc 系 path に届かない。

拦截は path 解決後・ファイル書き込み前。ログは元の tool 名と絶対 path を残し、汎用 permission denied ではなく reason outside_working_directories で SIEM 集計できるようにする。

permissions.blockWritesOutsideWorkingDirectories を settings JSON にどう書くか

ユーザーまたは托管 settings の permissions に blockWritesOutsideWorkingDirectories を true で追加する。开关は permission mode と直交し、bypassPermissions でも書き込み tool は workingDirectories 前缀检查を通る。

settings:permissions.blockWritesOutsideWorkingDirectories を true。
{
  "permissions": {
    "blockWritesOutsideWorkingDirectories": true
  }
}

开关を文字列 true にしたり permissions 階層を欠くと、ローカル schema は先に失敗して schema を返すべきで、write_path_gate の业务分支に入る前に止める。形状错误を not_enabled と混同しない。

セッションの workingDirectories と blockOutside の結び付け

クライアントはセッション context JSON に workingDirectories 配列と blockOutside 布尔を書く。blockOutside が true かつ settings 开关が on のときだけ前缀比对。false なら settings が true でも write_path_gate は not_enabled。

セッション context:workingDirectories と blockOutside。
{
  "workingDirectories": ["/home/dev/myapp"],
  "blockOutside": true
}

workingDirectories は少なくとも 1 つの非空文字列。空配列は no_workdirs。比对前に根の末尾スラッシュを除き、path が根と等しいか 根/ で始まるかを見る。

セッション context JSON Schema:形状を通してから write_path_gate。
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": false,
  "required": ["workingDirectories", "blockOutside"],
  "properties": {
    "workingDirectories": {
      "type": "array",
      "minItems": 1,
      "items": { "type": "string", "minLength": 1 }
    },
    "blockOutside": { "type": "boolean" }
  }
}

Write、Edit、NotebookEdit の tool 呼び出し JSON に要るフィールド

監査サンプルは最低 tool と path。tool が Write、Edit、NotebookEdit なら書き込み拦截 logic。path は絶対路径文字列。相対を解決する客户端ならログは解決後の絶対値を推奨し SIEM 规则を揃える。

許可:Write が workingDirectory 内ファイルを指す。
{
  "tool": "Write",
  "path": "/home/dev/myapp/src/main.ts"
}
拒否:Edit が workingDirectory 外 path を指す。
{
  "tool": "Edit",
  "path": "/etc/hosts"
}

拒否レコードの decision と reason の書き方

path がいずれの workingDirectory 前缀でもないとき客户端は書き込まず、permission または tool ログ JSON に decision denied と reason outside_working_directories を書き tool と path 原値を残す。NotebookEdit は Edit と同じ reason 枚举で三種の書き込み tool を一规则に集約。

拒否ログ:decision denied、reason outside_working_directories。
{
  "tool": "Edit",
  "path": "/etc/hosts",
  "decision": "denied",
  "reason": "outside_working_directories"
}
ログまたは呼び出し現象write_path_gate 戻り運用対応
path が workingDirectories 前缀内、tool が Write または NotebookEditallow実行許可。ログに outside_working_directories は不要。
path が根外、tool Edit、blockOutside truedeny_outside客户端ログの decision denied と reason outside_working_directories に合わせる。
blockOutside false または settings 开关未生效not_enabledoutside_working_directories と誤報しない。先に settings とセッション blockOutside を確認。
tool Bash、path 任意not_write_tool作業ディレクトリ書き込み拦截と分離集計。Bash は他の permission 规则の対象。

write_path_gate は schema と七业务分支をどう選ぶか

順序:セッション context JSON Schema で workingDirectories と blockOutside を検証;blockOutside false → not_enabled;call の path を読み非文字列 → missing_path;tool が Write、Edit、NotebookEdit 外 → not_write_tool;workingDirectories 空 → no_workdirs;path が前缀一致 → allow;それ以外 deny_outside、ログ reason outside_working_directories。

deny_outside と missing_path を blocked 一つに畳まない。レポートで分けて数え、path 誤り、tool 種別違い、本当の outside_working_directories 拒否を区別する。

write_path_gate:schema、not_enabled、missing_path、not_write_tool、no_workdirs、allow、deny_outside。
def actual_type(data: object) -> str:
    if data is None:
        return "null"
    if isinstance(data, bool):
        return "boolean"
    if isinstance(data, list):
        return "array"
    if isinstance(data, int):
        return "integer"
    if isinstance(data, float):
        return "number"
    if isinstance(data, str):
        return "string"
    if isinstance(data, dict):
        return "object"
    raise TypeError(type(data))

def validate(data: object, schema: dict) -> list[str]:
    errors: list[str] = []
    if not isinstance(schema, dict):
        return errors
    types = schema.get("type")
    if isinstance(types, str):
        types = [types]
    actual = actual_type(data)
    if types and not any(t == actual or (t == "number" and actual == "integer") for t in types):
        errors.append("type")
    if "enum" in schema and not any(value == data for value in schema["enum"]):
        errors.append("enum")
    if isinstance(data, str) and "minLength" in schema and len(data) < schema["minLength"]:
        errors.append("minLength")
    if isinstance(data, dict) and schema.get("properties"):
        for key in schema.get("required") or []:
            if key not in data:
                errors.append(f"required:{key}")
        if schema.get("additionalProperties") is False:
            for key in data:
                if key not in schema["properties"]:
                    errors.append(f"additional:{key}")
        for key, sub in schema["properties"].items():
            if key in data and data[key] is not None:
                errors.extend(validate(data[key], sub))
    return errors

def write_path_gate(ctx: dict, call: dict, schema: dict) -> str:
    if validate(ctx, schema):
        return "schema"
    if not ctx.get("blockOutside"):
        return "not_enabled"
    path = call.get("path")
    if not isinstance(path, str):
        return "missing_path"
    tool = call.get("tool")
    if tool not in {"Write", "Edit", "NotebookEdit"}:
        return "not_write_tool"
    roots = ctx.get("workingDirectories") or []
    if not roots:
        return "no_workdirs"
    for root in roots:
        base = root.rstrip("/")
        if path == base or path.startswith(base + "/"):
            return "allow"
    return "deny_outside"

JSONNote で settings、context、拒否ログをどう検証するか

  1. 1
    Schema で context 形状を固定

    開く JSON Schema。1欄にセッション context schema、2欄に workingDirectories と blockOutside サンプル。required や minItems 誤りを先に表示し、形状错误で allow や deny_outside を返さない。

  2. 2
    フォーマットで tool 呼び出しを確認

    開く JSON フォーマット。Write、Edit、NotebookEdit 呼び出し JSON を貼る。tool と path があり path が文字列か確認し、write_path_gate が missing_path になるか照合する。

  3. 3
    Diff で許可と拒否ログを比較

    開く JSON Diff。左に workingDirectory 内の許可サンプル、右に decision denied と reason outside_working_directories の拒否サンプル。Diff で光る reason が SIEM 规则の键行。

よくある質問

bypassPermissions モードで作業ディレクトリ外に書けるか?

いいえ。2.1.314 から blockWritesOutsideWorkingDirectories が true なら bypassPermissions でも Write、Edit、NotebookEdit は作業ディレクトリ外で path 前缀により止まり、ログは reason outside_working_directories のまま。bypass が目录检查を飛ばすと仮定しない。

シンボリックリンクで workingDirectories 前缀を bypass できるか?

客户端はリクエスト内 path 文字列で前缀比对し、ここでは symlink 先を展開しない。workingDirectories は実プロジェクト根にし、agent が送った path を監査に残す。本篇は検証逻辑のみで bypass や攻击手順は書かない。

permission policy の allow や deny 规则と冲突するか?

冲突ではなく別層。permission policy は tool 種別の默认許可か审批キューかを決め、blockWritesOutsideWorkingDirectories は書き込み tool が実行許可済みでも path で目录外を硬挡する。JSON は分けて保存し write_path_gate は目录層だけを镜像し policy の allow や deny は判定しない。

Read や Grep が作業ディレクトリ外を読むと outside_working_directories になるか?

ならない。outside_working_directories は Write、Edit、NotebookEdit の書き込み拒否のみ。Read 系 tool は別の permission と sandbox 规则。write_path_gate は not_write_tool を返し、読み取りを書き込み拒否指标に混ぜない。

no_workdirs と deny_outside をログでどう区別するか?

no_workdirs は workingDirectories が欠落または空のときのローカル闸门の設定错误分支で、outside_working_directories reason を偽造しない。deny_outside が客户端拒否した具体 path に対応。schema、no_workdirs、deny_outside を分けて集計し、根未設定を外泄書き込みと誤認しない。

まとめ

Claude Code 2.1.314 は permissions.blockWritesOutsideWorkingDirectories で Write、Edit、NotebookEdit を作業ディレクトリ前缀内に閉じ、拒否時は JSON ログに decision denied と reason outside_working_directories を書く。运维は settings、セッション context、tool 呼び出し、ログの四样本で write_path_gate を動かし permission mode 名だけを見ない。

結論:context が schema 不通 → schema。blockOutside false → not_enabled。path 非文字列 → missing_path。tool が Write、Edit、NotebookEdit 以外 → not_write_tool。workingDirectories 空 → no_workdirs。path が前缀内 → allow。それ以外 deny_outside、ログは decision denied と reason outside_working_directories。

次:Schema で context と settings を固定し、フォーマットで tool と path を見て、Diff で許可と拒否ログを比べ、write_path_gate 戻り値をレポートで分ける。

ブログへ戻る