Блог • Claude Code / Права

После обновления до Claude Code 2.1.314 с permissions.blockWritesOutsideWorkingDirectories: как фиксировать отклонённые path для Write и Edit в JSON-логах?

30 октября 2026 Anthropic выпустил Claude Code 2.1.314 с permissions.blockWritesOutsideWorkingDirectories. При true Write, Edit и NotebookEdit отклоняются вне workingDirectories в любом permission mode, включая default, acceptEdits и bypassPermissions.

Это не тот же слой, что allow или deny в permission policy: те управляют типами tool и интерактивным одобрением, запись вне каталогов блокируется по префиксу path до выполнения. Audit JSON должен хранить флаг settings, session workingDirectories, tool и path, decision denied и reason outside_working_directories для write_path_gate.

Очередь allow и deny — в Agent permission policy; контекст auto evaluation Managed Agents — в auto evaluation Managed Agents; JSON workflow сессии — в Claude Code workflow. Эта статья только о блокировках 2.1.314 вне working directories, форме полей лога и ветвлении write_path_gate.

Сначала это: 2.1.314 добавляет жёсткий блок записи на границе working directories и поля лога; таблицы allow или deny в permission policy не переписываются. Далее: changelog, settings JSON, session workingDirectories, образцы Write Edit NotebookEdit, логи отказа и таблица, исходник write_path_gate, проверка форм в JSONNote.

Как 2.1.314 блокирует Write, Edit и NotebookEdit вне working directories

Изменения — в changelog Claude Code, страница тега релиз v2.1.314. По сравнению с ранними версиями заметен permissions.blockWritesOutsideWorkingDirectories: включённый режим не пускает write tools к /etc через bypassPermissions или acceptEdits.

Блок после разрешения path и до записи файла. В логах нужны исходное имя tool и абсолютный path для агрегации SIEM по reason outside_working_directories, а не общий permission denied.

Как задать permissions.blockWritesOutsideWorkingDirectories в settings JSON

Добавьте blockWritesOutsideWorkingDirectories в permissions user или managed settings со значением true. Флаг ортогонален permission mode: даже в bypassPermissions write tools проходят проверку префикса workingDirectories.

Settings: permissions.blockWritesOutsideWorkingDirectories true.
{
  "permissions": {
    "blockWritesOutsideWorkingDirectories": true
  }
}

Если флаг записан строкой true или нет объекта permissions, schema validation сначала возвращает schema до business-веток write_path_gate. Не считать ошибку формы not_enabled.

Как session workingDirectories и blockOutside связаны

Клиент пишет массив workingDirectories и boolean blockOutside в JSON контекста сессии. Сравнение префикса только при blockOutside true и включённом settings; при blockOutside false write_path_gate → not_enabled, даже если settings true.

Контекст session: workingDirectories и blockOutside.
{
  "workingDirectories": ["/home/dev/myapp"],
  "blockOutside": true
}

workingDirectories требует хотя бы одну непустую строку; пустой массив → no_workdirs. Перед сравнением уберите trailing slash у корней; path равен корню или начинается с корень/.

JSON Schema context session: форма до 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" }
  }
}

Какие поля нужны в JSON вызова Write, Edit, NotebookEdit

Образцы аудита нужны минимум tool и path. Write, Edit или NotebookEdit входят в логику блокировки записи; path — абсолютная строка. Если клиент разрешает относительные пути, в логе храните абсолют после разрешения для SIEM.

Разрешено: Write на файл внутри workingDirectory.
{
  "tool": "Write",
  "path": "/home/dev/myapp/src/main.ts"
}
Отказ: Edit на path вне workingDirectory.
{
  "tool": "Edit",
  "path": "/etc/hosts"
}

Как писать decision и reason в записи отказа

Если path не под префиксом workingDirectory, клиент не пишет и логирует decision denied и reason outside_working_directories, сохраняя tool и path. NotebookEdit использует ту же enum reason, что Edit, для одного правила отчёта на три write tools.

Лог отказа: 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_enabledНе помечать outside_working_directories ошибочно; сначала settings и blockOutside сессии.
tool Bash, path где угодноnot_write_toolСчитать отдельно от блокировок записи по working directory; Bash по-прежнему под другими permission rules.

Как write_path_gate выбирает между schema и семью business-ветками

Порядок: validate session context JSON Schema для workingDirectories и blockOutside; blockOutside false → not_enabled; читать path вызова, не string → 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"

Как проверять settings, context и логи отказа в JSONNote

  1. 1
    Зафиксировать форму context через Schema

    Откройте JSON Schema. Schema context session в первое поле, образец workingDirectories и blockOutside во второе. Ошибки required или minItems сначала; при плохой форме не возвращать allow или deny_outside.

  2. 2
    Format для вызовов tool

    Откройте JSON Формат, вставьте JSON вызова Write, Edit или NotebookEdit. Убедитесь, что есть tool и path и path — string, прежде чем проверять missing_path у write_path_gate.

  3. 3
    Diff разрешённых и отказных логов

    Откройте JSON Diff. Слева образец allow под workingDirectory, справа отказ с decision denied и reason outside_working_directories. Подсвеченный reason — ключевая строка для SIEM.

Вопросы

Может ли bypassPermissions писать вне working directories?

Нет. С 2.1.314 при blockWritesOutsideWorkingDirectories true Write, Edit и NotebookEdit блокируются вне working directories даже в bypassPermissions; в логах reason outside_working_directories, не предполагайте, что bypass пропускает проверку каталогов.

Могут ли symlink обойти проверку префикса workingDirectories?

Клиент сравнивает префиксы по строке path в запросе, не разрешая цели symlink здесь. Задайте workingDirectories на реальные корни проекта и логируйте path от agent; статья описывает только logic validation, без шагов bypass или атаки.

Конфликтует ли это с allow или deny в permission policy?

Не конфликт, разные слои. permission policy решает allow или очередь одобрения для типа tool; blockWritesOutsideWorkingDirectories всё равно hard-block вне каталога по path. JSON храните раздельно; write_path_gate отражает только слой каталогов, не allow или deny policy.

Будут ли Read или Grep вне working directories логировать outside_working_directories?

Нет. outside_working_directories только для отказов записи Write, Edit, NotebookEdit. Read tools идут по другим permission и sandbox; write_path_gate → not_write_tool, не смешивайте чтение с метриками отказа записи.

Как в логах отличить no_workdirs от deny_outside?

no_workdirs — локальная ветка при отсутствии или пустых workingDirectories, не подделывайте reason outside_working_directories. deny_outside — конкретный path, который клиент отклонил. Отчитывайте schema, no_workdirs и deny_outside отдельно.

Итог

Claude Code 2.1.314 через permissions.blockWritesOutsideWorkingDirectories держит Write, Edit и NotebookEdit в префиксах working directory и при отказе пишет в JSON-лог decision denied и reason outside_working_directories. Кормите write_path_gate четырьмя образцами settings, context session, tool calls и logs, а не только именами permission mode.

Решение: context не проходит schema → schema; blockOutside false → not_enabled; path не string → missing_path; tool не Write, Edit или NotebookEdit → not_write_tool; пустые workingDirectories → no_workdirs; path под префиксом → allow; иначе deny_outside с log decision denied и reason outside_working_directories.

Дальше: Schema для context и settings, Format для tool и path, Diff разрешённых и отказных логов, отдельно отчитывать возвраты write_path_gate.

Назад в блог