Blog • Claude Code / Permissions
Après la mise à niveau vers Claude Code 2.1.314 avec permissions.blockWritesOutsideWorkingDirectories activé, comment consigner les chemins Write et Edit refusés dans les logs JSON ?
Le 30 octobre 2026, Anthropic a livré Claude Code 2.1.314 avec permissions.blockWritesOutsideWorkingDirectories. Si true, Write, Edit et NotebookEdit sont refusés hors workingDirectories dans chaque mode permission, y compris default, acceptEdits et bypassPermissions.
Ce n'est pas la même couche que allow ou deny dans une permission policy : celles-ci gèrent types d'outils et approbation, les écritures hors répertoires sont bloquées par préfixe de path avant exécution. Le JSON d'audit doit garder le flag settings, workingDirectories de session, tool et path, decision denied et reason outside_working_directories pour write_path_gate.
La file allow et deny est dans Agent permission policy; le contexte auto evaluation Managed Agents dans évaluation auto Managed Agents; le JSON workflow de session dans Claude Code workflow. Cet article ne couvre que les blocages 2.1.314 hors répertoires de travail, la forme des champs de log et les branches de write_path_gate.
- Si permissions.blockWritesOutsideWorkingDirectories est true dans settings, la session doit fournir des workingDirectories non vides, sinon write_path_gate renvoie no_workdirs.
- Le JSON d'appel Write, Edit, NotebookEdit doit avoir un path string ; absent ou non string → missing_path, pas outside_working_directories.
- Si path est sous un workingDirectory ou enfant → allow ; hors racine comme /etc/hosts → refus avec decision denied et reason outside_working_directories dans le log.
- Les tools non-écriture comme Bash et Read ne sont pas dans le set ; write_path_gate renvoie not_write_tool, à compter à part de deny_outside.
Gardez ceci : 2.1.314 ajoute un blocage dur des écritures aux limites des répertoires de travail et les champs de log associés ; il ne réécrit pas les tables allow ou deny d'une permission policy. Ensuite : changelog, JSON settings, workingDirectories de session, exemples Write Edit NotebookEdit, logs de refus et table, source write_path_gate, validation des formes dans JSONNote.
Comment 2.1.314 bloque Write, Edit et NotebookEdit hors répertoires de travail
Les notes de version sont dans changelog Claude Code, page tag version v2.1.314. Par rapport aux versions antérieures, permissions.blockWritesOutsideWorkingDirectories : activé, les tools d'écriture n'atteignent plus des chemins comme /etc via bypassPermissions ou acceptEdits.
Le blocage intervient après résolution du path et avant l'écriture. Les logs gardent le nom tool et le path absolu pour agréger sur reason outside_working_directories plutôt qu'un permission denied générique.
Comment écrire permissions.blockWritesOutsideWorkingDirectories dans le JSON settings
Ajoutez blockWritesOutsideWorkingDirectories sous permissions dans settings utilisateur ou gérés à true. Le drapeau est orthogonal au permission mode : même en bypassPermissions, les tools d'écriture passent le contrôle de préfixe workingDirectories.
{
"permissions": {
"blockWritesOutsideWorkingDirectories": true
}
}
Si le drapeau est la chaîne true ou permissions manque, la validation schema échoue d'abord avec schema avant les branches métier de write_path_gate. Ne pas confondre erreur de forme et not_enabled.
Comment workingDirectories et blockOutside se lient en session
Le client écrit un tableau workingDirectories et blockOutside booléen dans le JSON de contexte de session. Comparaison de préfixe seulement si blockOutside true et le flag settings actif ; si blockOutside false → not_enabled même si settings true.
{
"workingDirectories": ["/home/dev/myapp"],
"blockOutside": true
}
workingDirectories exige au moins une chaîne non vide ; tableau vide → no_workdirs. Retirez les slash finaux des racines avant de tester égalité ou préfixe root/.
{
"$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" }
}
}
Quels champs exige le JSON d'appel Write, Edit, NotebookEdit
Les échantillons d'audit exigent au minimum tool et path. Write, Edit ou NotebookEdit entrent dans la logique de blocage ; path doit être une chaîne absolue. Si le client résout des relatifs, le log garde la valeur absolue résolue pour les règles SIEM.
{
"tool": "Write",
"path": "/home/dev/myapp/src/main.ts"
}
{
"tool": "Edit",
"path": "/etc/hosts"
}
Comment écrire decision et reason sur un refus
Si path n'est sous aucun préfixe workingDirectory, le client n'écrit pas et logue decision denied et reason outside_working_directories en gardant tool et path d'origine. NotebookEdit partage la même enum reason qu'Edit pour une règle sur trois tools d'écriture.
{
"tool": "Edit",
"path": "/etc/hosts",
"decision": "denied",
"reason": "outside_working_directories"
}
| Signal log ou appel | write_path_gate renvoie | Action opérateur |
|---|---|---|
| path sous préfixe workingDirectories, tool Write ou NotebookEdit | allow | Autoriser ; pas de outside_working_directories dans le log. |
| path hors racines, tool Edit, blockOutside true | deny_outside | Aligner sur decision denied et reason outside_working_directories du log client. |
| blockOutside false ou flag settings inactif | not_enabled | Ne pas signaler outside_working_directories à tort ; vérifier settings et blockOutside de session. |
| tool Bash, path n'importe où | not_write_tool | Compter à part des blocages d'écriture hors répertoire ; Bash reste soumis aux autres permission rules. |
Comment write_path_gate choisit entre schema et sept branches métier
Ordre : valider le JSON Schema de contexte pour workingDirectories et blockOutside ; blockOutside false → not_enabled ; lire path de l'appel, non string → missing_path ; tool hors Write, Edit, NotebookEdit → not_write_tool ; workingDirectories vide → no_workdirs ; path correspond à un préfixe → allow ; sinon deny_outside et reason outside_working_directories dans le log.
Ne fusionnez pas deny_outside et missing_path en un seul blocked. Comptez séparément pour distinguer mauvais path, mauvais type tool et refus outside_working_directories réels.
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"
Comment valider settings, context et logs de refus dans JSONNote
- 1Fixer la forme context avec Schema
Ouvrez JSON Schema. Schema context session au premier champ, échantillon workingDirectories et blockOutside au second. Erreurs required ou minItems d'abord ; pas allow ou deny_outside si la forme est mauvaise.
- 2Format pour les appels tool
Ouvrez JSON Format, collez le JSON d'appel Write, Edit ou NotebookEdit. Vérifiez tool et path et que path est string avant de juger missing_path pour write_path_gate.
- 3Diff logs autorisés et refusés
Ouvrez JSON Diff. À gauche un échantillon autorisé sous workingDirectory, à droite un refus avec decision denied et reason outside_working_directories. Le reason surligné est la clé SIEM.
Questions fréquentes
Le mode bypassPermissions peut-il encore écrire hors répertoires de travail ?
Non. Dès 2.1.314 avec blockWritesOutsideWorkingDirectories true, Write, Edit et NotebookEdit restent bloqués hors répertoires de travail même en bypassPermissions ; les logs gardent reason outside_working_directories sans supposer que bypass ignore le contrôle de répertoire.
Les liens symboliques contournent-ils le préfixe workingDirectories ?
Le client compare les préfixes sur la chaîne path de la requête sans résoudre les cibles symlink ici. Fixez workingDirectories sur de vraies racines projet et loguez le path soumis par l'agent ; cet article décrit seulement la logique de validation, pas de contournement ni d'attaque.
Est-ce en conflit avec allow ou deny dans une permission policy ?
Pas de conflit, couches différentes. La permission policy décide allow ou file d'approbation par type tool ; blockWritesOutsideWorkingDirectories bloque encore hors répertoire par path. JSON séparés ; write_path_gate ne reflète que la couche répertoire, pas allow ou deny policy.
Read ou Grep hors répertoires de travail loguent-ils outside_working_directories ?
Non. outside_working_directories ne vise que les refus d'écriture Write, Edit, NotebookEdit. Les tools Read suivent d'autres permission et sandbox ; write_path_gate renvoie not_write_tool sans mélanger lecture et métriques de refus d'écriture.
Comment les logs séparent no_workdirs et deny_outside ?
no_workdirs est une branche locale quand workingDirectories manque ou est vide ; ne pas fabriquer reason outside_working_directories. deny_outside correspond à un path concret refusé par le client. Rapportez schema, no_workdirs et deny_outside séparément.
Conclusion
Claude Code 2.1.314 utilise permissions.blockWritesOutsideWorkingDirectories pour garder Write, Edit et NotebookEdit dans les préfixes de répertoires de travail et loguer decision denied et reason outside_working_directories au refus. Pilotez write_path_gate avec settings, contexte session, appels tool et logs, pas seulement les noms de permission mode.
Décision : context échoue schema → schema ; blockOutside false → not_enabled ; path non string → missing_path ; tool hors Write, Edit, NotebookEdit → not_write_tool ; workingDirectories vide → no_workdirs ; path sous préfixe → allow ; sinon deny_outside avec log decision denied et reason outside_working_directories.
Ensuite : Schema pour context et settings, Format pour tool et path, Diff logs autorisés et refusés, compter séparément les retours write_path_gate.