fix(permissions): 자식 디렉토리 settings.json 전파 + deny 패턴 스펙 교정

근본 원인 2종 해소:

1. .claude/ 계층 auto-merge 미지원 문제
   - Claude Code는 .claude/ 폴더 1개만 프로젝트 루트로 인식, 부모 auto-merge 안 함
   - 개발실/.claude/·기획실/.claude/ 이미 존재 → 자식 디렉토리 세션에서 루트 settings.json 무시됨
   - 기획실보다 작업량이 2~4배 복잡한 개발실에서 승인 요구 폭발적으로 드러남
   - 해결: 루트 SOT → 부서 2개 복제 배치 + setup_windows.ps1에 자동 동기화 로직 추가

2. deny 패턴 리터럴 매칭 문제
   - 'Bash(rm -rf /*)'는 glob이 아닌 리터럴로 해석되어 실제 rm -rf /home/foo 차단 불가
   - prefix 기반 'Bash(rm:*)' 'Bash(rmdir:*)' 'Bash(chown:*)' 등으로 교정
   - 추가 deny: shutdown/reboot/chmod 777/Windows 시스템 디렉토리 쓰기

memory/org/feedback_permissions_portability.md 보강:
- "자식 디렉토리 전파 원칙" 섹션 신설
- SOT(루트) + 파생(부서 2개) + 동기화 메커니즘 명시

세션 재시작 후 적용됨.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-04-15 10:03:26 +09:00
parent 4a15ed28ad
commit 356b473ea9
5 changed files with 196 additions and 11 deletions

View File

@ -1,11 +1,10 @@
{
"_description": "너드나비스 조직 공용 Claude Code permission 설정. PD님의 일괄 승인 원칙(memory/org/feedback_md_approval.md·feedback_approval_process.md)을 harness 차원에서 구현. 모든 PC에서 동일하게 적용되도록 git 커밋 대상. 개별 PC 예외·실험적 명령은 .claude/settings.local.json(gitignore)에 기록.",
"_description": "너드나비스 조직 공용 Claude Code permission 설정 (SOT). PD님의 일괄 승인 원칙(memory/org/feedback_md_approval.md·feedback_approval_process.md)을 harness 차원에서 구현. 모든 PC에서 동일하게 적용되도록 git 커밋 대상. 개발실/·기획실/에도 동일 사본이 배치되어야 자식 디렉토리에서 세션을 시작해도 동일 권한이 적용됨 (Claude Code는 .claude/ 계층 auto-merge 미지원). 동기화는 setup_windows.ps1이 수행.",
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"LS",
"TodoWrite",
"Edit",
"Write",
@ -40,8 +39,9 @@
"Bash(false)",
"Bash(powershell.exe:*)",
"Bash(powershell:*)",
"Bash(fsutil reparsepoint:*)",
"Bash(npm run:*)",
"Bash(fsutil:*)",
"Bash(cmd:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(python:*)",
@ -52,21 +52,24 @@
"WebSearch"
],
"deny": [
"Bash(rm -rf /*)",
"Bash(rm -rf ~*)",
"Bash(rm -rf $HOME*)",
"Bash(rm:*)",
"Bash(rmdir:*)",
"Bash(sudo:*)",
"Bash(chmod 777:*)",
"Bash(format:*)",
"Bash(dd:*)",
"Bash(mkfs:*)",
"Bash(:(){ :|:& };:)",
"Bash(format:*)",
"Bash(chmod 777:*)",
"Bash(chown:*)",
"Bash(shutdown:*)",
"Bash(reboot:*)",
"Write(/etc/**)",
"Write(/System/**)",
"Write(C:/Windows/**)",
"Write(C:\\Windows\\**)",
"Edit(/etc/**)",
"Edit(/System/**)",
"Edit(C:/Windows/**)"
"Edit(C:/Windows/**)",
"Edit(C:\\Windows\\**)"
]
}
}

View File

@ -19,6 +19,18 @@ Claude Code의 승인 체계는 **2계층**으로 분리되어 있다:
**결론**: 조직 공용 permission은 반드시 `.claude/settings.json`(프로젝트 루트)에 선언·커밋해야 PC 독립성이 보장된다.
## 자식 디렉토리 전파 원칙 (너드나비스 구조 특수사항)
Claude Code는 `.claude/` 폴더 1개만 프로젝트 루트로 인식하며 **부모 방향 auto-merge를 지원하지 않는다**. 너드나비스 조직 레포는 루트 외에 `개발실/.claude/`·`기획실/.claude/`가 이미 존재(agents·commands·skill-modules 용). 이 때문에 자식 디렉토리에서 세션을 시작하면 루트의 settings.json이 무시된다.
**해결 구조**:
- **SOT**: `.claude/settings.json` (루트). 모든 변경은 이 파일을 먼저 편집.
- **파생**: `개발실/.claude/settings.json`·`기획실/.claude/settings.json` — 루트 SOT의 복사본.
- **동기화**: `setup/setup_windows.ps1`이 루트 SOT → 두 부서로 자동 복제. 셋업 스크립트 실행 시마다 갱신.
- **수동 동기화 (스크립트 미실행 환경)**: 루트 settings.json 수정 후 두 부서 파일에도 동일 내용 반영 후 일괄 커밋.
**검증법**: 각 부서 디렉토리에서 `.claude/settings.json` 존재 여부 확인. 파일이 같은 내용인지 diff로 점검.
## 권장 구성
```json

View File

@ -98,4 +98,24 @@ if ($hashDirs.Count -eq 0) {
Write-Warning "Claude 프로젝트 해시 폴더를 찾지 못했습니다. 수동 연결 필요."
}
# 4. .claude/settings.json 부서 동기화 (루트 SOT → 개발실/기획실 복제)
# Claude Code는 .claude/ 계층 auto-merge를 지원하지 않으므로 자식 디렉토리에서 세션 시작 시
# 루트 settings.json을 인식하지 못함. 이를 우회하기 위해 루트 settings.json을 부서 디렉토리로 복제.
$rootSettings = Join-Path $NerdNavisRoot ".claude\settings.json"
if (Test-Path $rootSettings) {
$deptPaths = @("개발실", "기획실")
foreach ($dept in $deptPaths) {
$deptClaudeDir = Join-Path $NerdNavisRoot "$dept\.claude"
$deptSettings = Join-Path $deptClaudeDir "settings.json"
if (-not (Test-Path $deptClaudeDir)) {
New-Item -ItemType Directory -Path $deptClaudeDir -Force | Out-Null
}
Copy-Item -Path $rootSettings -Destination $deptSettings -Force
Write-Host "settings.json 동기화: $dept/.claude/settings.json"
}
} else {
Write-Warning "루트 .claude/settings.json 미발견. 부서 동기화 생략."
}
Write-Host "셋업 완료. 'git pull'로 최신 상태 유지 권장."
Write-Host "※ .claude/settings.json 변경사항은 세션 재시작 후 적용됨."

View File

@ -0,0 +1,75 @@
{
"_description": "너드나비스 조직 공용 Claude Code permission 설정 (SOT). PD님의 일괄 승인 원칙(memory/org/feedback_md_approval.md·feedback_approval_process.md)을 harness 차원에서 구현. 모든 PC에서 동일하게 적용되도록 git 커밋 대상. 개발실/·기획실/에도 동일 사본이 배치되어야 자식 디렉토리에서 세션을 시작해도 동일 권한이 적용됨 (Claude Code는 .claude/ 계층 auto-merge 미지원). 동기화는 setup_windows.ps1이 수행.",
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"TodoWrite",
"Edit",
"Write",
"MultiEdit",
"NotebookEdit",
"Bash(git:*)",
"Bash(gh:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(grep:*)",
"Bash(rg:*)",
"Bash(find:*)",
"Bash(echo:*)",
"Bash(pwd)",
"Bash(cd:*)",
"Bash(mkdir:*)",
"Bash(touch:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(diff:*)",
"Bash(wc:*)",
"Bash(sort:*)",
"Bash(uniq:*)",
"Bash(tr:*)",
"Bash(sed:*)",
"Bash(awk:*)",
"Bash(xxd:*)",
"Bash(printf:*)",
"Bash(true)",
"Bash(false)",
"Bash(powershell.exe:*)",
"Bash(powershell:*)",
"Bash(fsutil:*)",
"Bash(cmd:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(python:*)",
"Bash(python3:*)",
"Bash(pytest:*)",
"Bash(dotnet:*)",
"WebFetch",
"WebSearch"
],
"deny": [
"Bash(rm:*)",
"Bash(rmdir:*)",
"Bash(sudo:*)",
"Bash(dd:*)",
"Bash(mkfs:*)",
"Bash(format:*)",
"Bash(chmod 777:*)",
"Bash(chown:*)",
"Bash(shutdown:*)",
"Bash(reboot:*)",
"Write(/etc/**)",
"Write(/System/**)",
"Write(C:/Windows/**)",
"Write(C:\\Windows\\**)",
"Edit(/etc/**)",
"Edit(/System/**)",
"Edit(C:/Windows/**)",
"Edit(C:\\Windows\\**)"
]
}
}

View File

@ -0,0 +1,75 @@
{
"_description": "너드나비스 조직 공용 Claude Code permission 설정 (SOT). PD님의 일괄 승인 원칙(memory/org/feedback_md_approval.md·feedback_approval_process.md)을 harness 차원에서 구현. 모든 PC에서 동일하게 적용되도록 git 커밋 대상. 개발실/·기획실/에도 동일 사본이 배치되어야 자식 디렉토리에서 세션을 시작해도 동일 권한이 적용됨 (Claude Code는 .claude/ 계층 auto-merge 미지원). 동기화는 setup_windows.ps1이 수행.",
"permissions": {
"allow": [
"Read",
"Glob",
"Grep",
"TodoWrite",
"Edit",
"Write",
"MultiEdit",
"NotebookEdit",
"Bash(git:*)",
"Bash(gh:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(grep:*)",
"Bash(rg:*)",
"Bash(find:*)",
"Bash(echo:*)",
"Bash(pwd)",
"Bash(cd:*)",
"Bash(mkdir:*)",
"Bash(touch:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(diff:*)",
"Bash(wc:*)",
"Bash(sort:*)",
"Bash(uniq:*)",
"Bash(tr:*)",
"Bash(sed:*)",
"Bash(awk:*)",
"Bash(xxd:*)",
"Bash(printf:*)",
"Bash(true)",
"Bash(false)",
"Bash(powershell.exe:*)",
"Bash(powershell:*)",
"Bash(fsutil:*)",
"Bash(cmd:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(python:*)",
"Bash(python3:*)",
"Bash(pytest:*)",
"Bash(dotnet:*)",
"WebFetch",
"WebSearch"
],
"deny": [
"Bash(rm:*)",
"Bash(rmdir:*)",
"Bash(sudo:*)",
"Bash(dd:*)",
"Bash(mkfs:*)",
"Bash(format:*)",
"Bash(chmod 777:*)",
"Bash(chown:*)",
"Bash(shutdown:*)",
"Bash(reboot:*)",
"Write(/etc/**)",
"Write(/System/**)",
"Write(C:/Windows/**)",
"Write(C:\\Windows\\**)",
"Edit(/etc/**)",
"Edit(/System/**)",
"Edit(C:/Windows/**)",
"Edit(C:\\Windows\\**)"
]
}
}