Блог • 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.
- Если permissions.blockWritesOutsideWorkingDirectories true в settings, session должна дать непустые workingDirectories, иначе write_path_gate → no_workdirs.
- JSON вызова Write, Edit, NotebookEdit должен содержать строковый path; если нет или не string → missing_path, не outside_working_directories.
- Если path под workingDirectory или ниже → allow; вне, например /etc/hosts → отказ с decision denied и reason outside_working_directories в логе.
- Tool без записи вроде Bash и Read вне набора блокировки; write_path_gate → not_write_tool, считать отдельно от deny_outside.
Сначала это: 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.
{
"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.
{
"workingDirectories": ["/home/dev/myapp"],
"blockOutside": true
}
workingDirectories требует хотя бы одну непустую строку; пустой массив → no_workdirs. Перед сравнением уберите trailing slash у корней; path равен корню или начинается с корень/.
{
"$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.
{
"tool": "Write",
"path": "/home/dev/myapp/src/main.ts"
}
{
"tool": "Edit",
"path": "/etc/hosts"
}
Как писать decision и reason в записи отказа
Если path не под префиксом workingDirectory, клиент не пишет и логирует decision denied и reason outside_working_directories, сохраняя tool и path. NotebookEdit использует ту же enum reason, что Edit, для одного правила отчёта на три write tools.
{
"tool": "Edit",
"path": "/etc/hosts",
"decision": "denied",
"reason": "outside_working_directories"
}
| Сигнал лога или вызова | write_path_gate возвращает | Действие оператора |
|---|---|---|
| path под префиксом workingDirectories, tool Write или NotebookEdit | allow | Разрешить выполнение; в логе не нужен outside_working_directories. |
| path вне корней, tool Edit, blockOutside true | deny_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.
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Зафиксировать форму context через Schema
Откройте JSON Schema. Schema context session в первое поле, образец workingDirectories и blockOutside во второе. Ошибки required или minItems сначала; при плохой форме не возвращать allow или deny_outside.
- 2Format для вызовов tool
Откройте JSON Формат, вставьте JSON вызова Write, Edit или NotebookEdit. Убедитесь, что есть tool и path и path — string, прежде чем проверять missing_path у write_path_gate.
- 3Diff разрешённых и отказных логов
Откройте 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.