settings.json Permissions: allow, deny und ask schneiden
Claude Code Permissions in settings.json: Auswertungsreihenfolge, Wildcards, Bash-Grenzen, Read- und Edit-Pfade und Scopes, ohne dass Regeln dich täuschen.
Nach ein paar Wochen mit Claude Code sieht die permissions.allow-Liste bei den meisten gleich aus: ein Haufen Einträge aus „Yes, and don’t ask again”, ein paar breite Wildcards, irgendwo ein deny für git push. Die Prompts sind weg, aber niemand kann sagen, was der Agent eigentlich darf. Dabei ist das Regelwerk präziser, als es aussieht, und an ein paar Stellen anders, als man intuitiv annimmt. Stand ist die offizielle Doku im Oktober 2026.
Die Reihenfolge: deny, dann ask, dann allow
Regeln werden in fester Reihenfolge geprüft: zuerst deny, dann ask, dann allow. Die erste passende Kategorie entscheidet. Spezifität spielt keine Rolle. Ein deny auf Bash(aws *) blockt also auch aws s3 ls, selbst wenn du genau das explizit erlaubt hast. Ausnahmen aus einem Deny kannst du mit Allow-Regeln nicht herausschneiden, und ein passendes ask fragt auch dann, wenn ein engeres allow existiert.
Das gilt über alle Scopes hinweg. Ein Deny in den User-Settings schlägt ein Allow im Projekt und umgekehrt. Managed Settings stehen ganz oben und lassen sich auch per CLI-Flag nicht überschreiben.
Ein Detail, das oft übersehen wird: Ein Deny auf den nackten Tool-Namen (Bash, oder mcp__* für alle MCP-Tools) entfernt das Tool komplett aus Claudes Kontext. Ein Deny mit Specifier wie Bash(rm *) lässt das Tool sichtbar und blockt nur passende Aufrufe.
Wildcards: der Stern gehört hinter das Subcommand
* matcht beliebigen Text, auch Leerzeichen. Alles vor dem ersten Stern wird wörtlich verglichen, deshalb entscheidet die Position:
// .claude/settings.json
{
"permissions": {
"allow": ["Bash(npm run *)", "Bash(git commit *)", "Bash(git log *)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Read(./secrets/**)"]
}
}
Bash(git log *) erlaubt nur git log, Bash(git *) erlaubt jedes git-Kommando. Noch gefährlicher ist ein Stern vor dem Subcommand: Bash(git * main) passt auch auf git push origin main und auf Varianten mit -c, mit denen git ein beliebiges Programm startet. Claude Code warnt beim Start vor solchen Allow-Regeln.
Zwei Feinheiten: Ein abschließendes * mit Leerzeichen davor matcht auch den nackten Befehl (Bash(ls *) trifft ls, aber nicht lsof). Ohne Leerzeichen (Bash(ls*)) trifft es beides. Die Schreibweise :* am Ende ist gleichwertig zu *.
Was eine Bash-Regel nicht leistet
Claude Code zerlegt zusammengesetzte Befehle an &&, ||, ;, | und Zeilenumbrüchen, und eine Allow-Regel muss jeden Teil einzeln abdecken. Deny- und Ask-Regeln greifen auch in Subshells, Command Substitutions und Schleifen. Einige Wrapper wie timeout, nice oder nohup werden vor dem Abgleich entfernt.
Trotzdem bleibt eine Bash-Regel ein Textvergleich und keine Sicherheitsgrenze um ein Programm. Ein Deny auf Bash(rm *) stoppt rm -rf build/, aber nicht /bin/rm -rf build/ oder bash -c 'rm -rf build/'. Ein Deny auf Bash(git push *) stoppt nicht git -C . push. Laufzeit-Wrapper wie npx, docker exec oder devbox run stehen nicht auf der Strip-Liste: Bash(devbox run *) erlaubt also alles, was dahinter folgt. Schreib dafür lieber einzelne Regeln mit dem konkreten inneren Befehl.
Wenn du echte Durchsetzung brauchst, gibt es zwei Ebenen darüber: einen PreToolUse-Hook, der den vollständigen Befehl mit eigener Logik prüft (siehe Claude Code Hooks), und die Sandbox, die Dateisystem- und Netzwerkzugriff auf OS-Ebene begrenzt.
Read- und Edit-Regeln: Pfade sind relativer, als du denkst
Dateizugriffe steuerst du nur über Read(...) und Edit(...). Regeln für Write, Glob oder NotebookEdit mit Pfad werden akzeptiert, aber nie ausgewertet. Ein Read-Deny blockt auch Edit und Write auf denselben Pfad, NotebookEdit allerdings nicht. Eine .claudeignore hat keine Wirkung, deren Einträge gehören in Read-Deny-Regeln.
Die Pfad-Syntax folgt gitignore mit vier Ankern:
//pfadist absolut ab Root,~/pfadrelativ zum Home-Verzeichnis,/pfadrelativ zur Quelle der Settings-Datei,pfadoder./pfadrelativ zum aktuellen Verzeichnis.
Die Falle steckt im einfachen Slash. Read(/secrets/**) in deinen User-Settings blockt ~/.claude/secrets/**, nicht den secrets-Ordner im Projekt. Wer eine Regel für alle Projekte will, nimmt // oder ~/.
Und auch hier gilt: Read- und Edit-Denies greifen für die eingebauten Datei-Tools und für Bash-Befehle, die Claude Code als Dateizugriff erkennt (cat, head, sed, Redirections). Ein Python-Skript, das selbst Dateien öffnet, oder ein grep -r ohne konkreten Dateinamen umgeht sie. Für echten Schutz braucht es die Sandbox.
Scopes, Trust und Modes
Wenn du bei einem Prompt „don’t ask again” wählst, landet die Regel in .claude/settings.local.json im Root des Repos und gilt damit auch in Worktrees und Unterverzeichnissen. Allow-Regeln aus einer committeten .claude/settings.json wirken erst, wenn du den Workspace-Trust-Dialog akzeptiert hast. Deny und Ask gelten sofort, weil sie nur einschränken. In claude -p erscheint kein Trust-Dialog, Projekt-Allow-Regeln greifen dort also nicht.
Für Subagents gibt es eigene Regeln: Agent(Explore) im Deny schaltet den eingebauten Explore-Agenten ab. Auch Parameter lassen sich prüfen, etwa Agent(model:opus) oder Agent(isolation:worktree), allerdings nur in Deny- und Ask-Regeln. Mehr zu Subagents steht im Artikel Claude Code Subagents.
Zum Schluss der Mode: bypassPermissions überspringt Prompts, auch für Schreibzugriffe auf .git und .claude, und gehört laut Doku nur in Container oder VMs. Mit permissions.disableBypassPermissionsMode: "disable" kannst du dich selbst davor schützen.
Was das für deinen Workflow bedeutet
Behandle deine Permissions wie Code, nicht wie eine Sammlung angeklickter Ausnahmen:
- Räum die angesammelten Allow-Einträge auf und ersetz breite Wildcards wie
Bash(git *)durch Subcommand-Regeln. - Setz Denies für Secrets mit dem richtigen Anker und prüf sie mit
/permissions, wo jede Regel samt Quelldatei steht. - Leg gemeinsame Regeln in
.claude/settings.json, persönliche insettings.local.json. - Alles, was echt sicherheitsrelevant ist, sicherst du zusätzlich per Hook oder Sandbox ab, nicht nur per Textregel.
Eine Checkliste der Dateien, die zu einem durchdachten Setup gehören, findest du unter /setup.
Du bist nicht sicher, ob deine permissions-Blöcke halten, was sie versprechen? Fordere ein Agent-Setup-Review an. Es geht deine settings.json, Scopes und Hooks durch und liefert konkrete Änderungsvorschläge.