settings.local.json 허용 규칙이 안 먹을 때
settings.local.json 에 Bash 허용 규칙을 넣었는데 계속 묻는다면 확인할 곳 — 불러온 파일, 규칙 문법, 복합 명령, deny 우선순위를 claude -p 로 직접 돌려 가린다.
.claude/settings.local.json 에 Bash(touch:*) 를 넣었는데 touch 를 실행할 때마다 허용 여부를 묻습니다. 파일 문제인지 규칙 문제인지 순서대로 가립니다.
[1] 세션 안에서 /status 를 실행하고 Setting sources 줄에 Project local settings 가 있는지 봅니다. 문서는 이 줄이 이번 세션이 불러온 설정 파일을 나열한다고 설명합니다. 없으면 규칙 이전에 파일이 읽히지 않은 것입니다.
[2] 파일 위치와 JSON 형식을 확인합니다. 프로젝트 로컬 파일은 프로젝트의 .claude/settings.local.json 입니다. git 저장소의 하위 폴더에서 시작하면 문서상 저장소 루트의 같은 파일을 읽습니다.
{ "permissions": { "allow": [ "Bash(touch:*)" ] }}[3] 파일이 git 에 추적되는지 봅니다. 문서에 따르면 settings.local.json 이 추적되거나 .claude 가 심볼릭 링크이면 저장소가 준 파일로 취급해서, 폴더를 신뢰하기 전까지 허용 규칙을 적용하지 않습니다. 직접 만든 파일은 .gitignore 에 넣어 둡니다. -p 실행은 신뢰한 것으로 치므로 이 경우는 대화형 세션에서만 나타납니다.
[4] 규칙이 실제로 먹는지 돌려 봅니다. git status 같은 읽기 전용 명령은 규칙이 없어도 묻지 않으므로 시험이 안 됩니다. 여기서는 touch 를 씁니다. --output-format json 의 permission_denials 에 거부된 호출이 남습니다.
$ cat .claude/settings.local.json{"permissions":{"allow":["Bash(touch:*)"]}}$ claude -p "Run exactly this shell command and nothing else, then reply with only OK or the reason it did not run: touch a.txt" --max-turns 3 --output-format json > r1.json$ jq -c '.permission_denials' r1.json[]$ ls *.txta.txta.txt 가 만들어졌고 거부는 없습니다. 이 버전에서는 settings.local.json 의 허용 규칙이 적용됩니다. GitHub 이슈 #32838 은 같은 위치의 Bash(rm:*) 가 /permissions 목록에는 보이는데 계속 묻는다고 보고했지만, 메인테이너 답변 없이 중복으로 닫혀 원인은 밝혀지지 않았습니다. 같은 증상이면 아래 [5]~[7] 의 규칙 쪽 원인부터 지웁니다.
[5] :* 는 규칙 맨 끝에서만 와일드카드입니다. 문서는 Bash(git:* push) 의 콜론이 문자 그대로 취급돼 git 명령과 맞지 않는다고 적습니다.
$ cat .claude/settings.local.json{"permissions":{"allow":["Bash(touch:* d.txt)"]}}$ claude -p "Run exactly this shell command and nothing else, then reply with only OK or the reason it did not run: touch d.txt" --max-turns 3 --output-format json > r3.json$ jq -c '.permission_denials[].tool_input.command' r3.json"touch d.txt"Bash(touch d.txt) 나 Bash(touch *) 로 고칩니다. 끝에서는 :* 와 * 가 같은 뜻입니다. 공백도 규칙의 일부라서 Bash(ls *) 는 lsof 에 맞지 않고 Bash(ls*) 는 맞습니다.
[6] && · || · ; · | 로 이어진 명령은 각 부분이 따로 허용돼야 합니다. Bash(touch:*) 만 있는 상태에서 rm 이 붙은 명령을 보냅니다.
$ cat .claude/settings.local.json{"permissions":{"allow":["Bash(touch:*)"]}}$ claude -p "Run exactly this shell command and nothing else, then reply with only OK or the reason it did not run: touch c.txt && rm -f a.txt" --max-turns 3 --output-format json > r2.json$ jq -c '.permission_denials[].tool_input.command' r2.json"touch c.txt && rm -f a.txt"$ ls *.txta.txtc.txt 는 안 생겼고 a.txt 는 그대로입니다. 허용된 touch 부분도 실행되지 않고 명령 전체가 막혔습니다. 묻는 이유는 touch 가 아니라 rm 입니다. rm 규칙을 더하거나 명령을 나눠 실행합니다.
[7] 같은 명령에 deny 가 걸려 있는지 봅니다. 평가 순서는 deny → ask → allow 이고 먼저 맞은 것이 결정합니다. 문서는 다른 설정 파일의 deny 도 allow 보다 먼저라고 적습니다. 허용은 로컬 파일에, 거부는 공유 settings.json 에 두고 돌려 봅니다.
$ cat .claude/settings.local.json .claude/settings.json{"permissions":{"allow":["Bash(touch:*)"]}}{"permissions":{"deny":["Bash(touch e.txt)"]}}$ claude -p "Run exactly this shell command and nothing else, then reply with only OK or the reason it did not run: touch e.txt" --max-turns 3 --output-format json > r4.json$ jq -c '.permission_denials[].tool_input.command' r4.json"touch e.txt"로컬 파일에 allow 를 더해도 다른 쪽 deny 는 덮이지 않습니다. deny 쪽 설명은 Claude Code 에서 .env 읽기를 deny 규칙으로 막기 에 있습니다.
[8] 설정을 고친 뒤 재시작이 필요한지는 문서를 따릅니다. Claude Code 는 설정 파일을 감시하다가 permissions 편집을 실행 중인 세션에 바로 적용합니다. 세션을 시작할 때 폴더가 이미 있었다면 중간에 만든 파일도 불러옵니다. 그래도 반영이 안 보이면 세션을 다시 시작하고 /status 로 [1] 을 다시 봅니다. 같은 키(permissions.allow)는 파일끼리 덮어쓰지 않고 합쳐집니다.