Просмотр исходного кода

chore: Gitignore + untrack .claude/settings.local.json (#8)

* chore: Gitignore .claude/settings.local.json + setperms safety step

settings.local.json is user-specific and routinely accumulates secrets in
permission rules (API keys baked into Bash allow entries). It must never
reach a remote.

- Add .claude/settings.local.json to repo .gitignore
- setperms now ensures the gitignore rule + untracks any prior copy (Step 3b)

Pairs with the git-ops push-safety gate, which also refuses pushes that add
this file — defense in depth so a leaked key can't recur.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: Untrack .claude/settings.local.json

The file was committed to history and is now gitignored, but remained
tracked at the tip. Remove it from the index so the gitignore rule takes
effect and the push-safety gate stops flagging it. The local file is kept.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: 0xDarkMatter <0xDarkMatter@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0xDarkMatter 2 недель назад
Родитель
Сommit
44fc18cbc0
3 измененных файлов с 25 добавлено и 184 удалено
  1. 0 184
      .claude/settings.local.json
  2. 3 0
      .gitignore
  3. 22 0
      skills/setperms/SKILL.md

+ 0 - 184
.claude/settings.local.json

@@ -1,184 +0,0 @@
-{
-  "env": {
-    "ENABLE_TOOL_SEARCH": "true",
-    "ENABLE_LSP_TOOL": "true"
-  },
-  "permissions": {
-    "allow": [
-      "WebSearch",
-      "WebFetch(domain:*)",
-      "Skill(*)",
-      "SlashCommand(*)",
-      "mcp__vibe_kanban__*",
-      "mcp__claude-in-chrome__*",
-      "Bash(git:*)",
-      "Bash(gh:*)",
-      "Bash(lazygit:*)",
-      "Bash(rg:*)",
-      "Bash(fd:*)",
-      "Bash(fzf:*)",
-      "Bash(grep:*)",
-      "Bash(find:*)",
-      "Bash(which:*)",
-      "Bash(where:*)",
-      "Bash(command:*)",
-      "Bash(cat:*)",
-      "Bash(bat:*)",
-      "Bash(head:*)",
-      "Bash(tail:*)",
-      "Bash(less:*)",
-      "Bash(more:*)",
-      "Bash(ls:*)",
-      "Bash(eza:*)",
-      "Bash(dir:*)",
-      "Bash(tree:*)",
-      "Bash(broot:*)",
-      "Bash(br:*)",
-      "Bash(mkdir:*)",
-      "Bash(cp:*)",
-      "Bash(mv:*)",
-      "Bash(rm:*)",
-      "Bash(touch:*)",
-      "Bash(chmod:*)",
-      "Bash(echo:*)",
-      "Bash(printf:*)",
-      "Bash(wc:*)",
-      "Bash(sort:*)",
-      "Bash(uniq:*)",
-      "Bash(cut:*)",
-      "Bash(awk:*)",
-      "Bash(sed:*)",
-      "Bash(sd:*)",
-      "Bash(tr:*)",
-      "Bash(xargs:*)",
-      "Bash(tee:*)",
-      "Bash(jq:*)",
-      "Bash(yq:*)",
-      "Bash(curl:*)",
-      "Bash(wget:*)",
-      "Bash(http:*)",
-      "Bash(firecrawl:*)",
-      "Bash(markitdown:*)",
-      "Bash(python:*)",
-      "Bash(python3:*)",
-      "Bash(pip:*)",
-      "Bash(pip3:*)",
-      "Bash(uv:*)",
-      "Bash(pytest:*)",
-      "Bash(node:*)",
-      "Bash(npm:*)",
-      "Bash(npx:*)",
-      "Bash(pnpm:*)",
-      "Bash(yarn:*)",
-      "Bash(bun:*)",
-      "Bash(just:*)",
-      "Bash(make:*)",
-      "Bash(cargo:*)",
-      "Bash(go:*)",
-      "Bash(rustc:*)",
-      "Bash(docker:*)",
-      "Bash(docker-compose:*)",
-      "Bash(tokei:*)",
-      "Bash(hyperfine:*)",
-      "Bash(dust:*)",
-      "Bash(procs:*)",
-      "Bash(btm:*)",
-      "Bash(bottom:*)",
-      "Bash(delta:*)",
-      "Bash(difft:*)",
-      "Bash(diff:*)",
-      "Bash(ast-grep:*)",
-      "Bash(sg:*)",
-      "Bash(tldr:*)",
-      "Bash(man:*)",
-      "Bash(z:*)",
-      "Bash(zoxide:*)",
-      "Bash(cd:*)",
-      "Bash(pwd:*)",
-      "Bash(env:*)",
-      "Bash(export:*)",
-      "Bash(source:*)",
-      "Bash(set:*)",
-      "Bash(test:*)",
-      "Bash(ps:*)",
-      "Bash(kill:*)",
-      "Bash(pkill:*)",
-      "Bash(pgrep:*)",
-      "Bash(whoami:*)",
-      "Bash(hostname:*)",
-      "Bash(uname:*)",
-      "Bash(date:*)",
-      "Bash(time:*)",
-      "Bash(ping:*)",
-      "Bash(netstat:*)",
-      "Bash(ss:*)",
-      "Bash(ip:*)",
-      "Bash(ifconfig:*)",
-      "Bash(systeminfo:*)",
-      "Bash(tar:*)",
-      "Bash(zip:*)",
-      "Bash(unzip:*)",
-      "Bash(gzip:*)",
-      "Bash(gunzip:*)",
-      "Bash(claude:*)",
-      "Bash(gemini:*)",
-      "Bash(codex:*)",
-      "Bash(perplexity:*)",
-      "Bash(grok:*)",
-      "Bash(glm:*)",
-      "Bash(droid:*)",
-      "Bash(opencode:*)",
-      "Bash(powershell:*)",
-      "Bash(pwsh:*)",
-      "Bash(cmd:*)",
-      "Bash(bash:*)",
-      "Bash(sh:*)",
-      "Bash(zsh:*)",
-      "Bash(canvas-tui:*)",
-      "Bash(brew:*)",
-      "Bash(apt:*)",
-      "Bash(apt-get:*)",
-      "Bash(choco:*)",
-      "Bash(scoop:*)",
-      "Bash(winget:*)",
-      "Bash(powershell -ExecutionPolicy Bypass -File \"./scripts/install.ps1\")",
-      "Bash(just.exe test:*)",
-      "Bash(Select-String \"Results:\")",
-      "Bash(for skill in atomise cli-patterns explain screenshot setperms skill-creator spawn techdebt)",
-      "Bash(do cp -r \"C:/Projects/claude-mods/skills/$skill\" \"C:/Users/<user>/.claude/skills/$skill\")",
-      "Bash(done)",
-      "Bash(do basename:*)",
-      "Bash(/tmp/skill_analysis.txt:*)",
-      "Read(//tmp/**)",
-      "Bash(do mkdir:*)",
-      "Bash(for skill in go-ops rust-ops typescript-ops docker-ops ci-cd-ops api-design-ops)",
-      "Bash(do echo \"=== $skill ===\")",
-      "Read(//x/Forge/claude-mods/skills/$skill/**)",
-      "Read(//x/Forge/claude-mods/**)",
-      "Bash(mv .git/info/exclude.tmp .git/info/exclude tail -5 .git/info/exclude echo \"\" echo \"=== Status ===\" git status --short)",
-      "PowerShell(Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -match '^\\(bash|sh|wsl|cmd|pwsh|powershell|conhost|node|python\\)$' } | Select-Object Id, ProcessName, StartTime, @{N='Path';E={$_.Path}} | Sort-Object StartTime -Descending | Select-Object -First 20 | Format-Table -AutoSize)",
-      "PowerShell(Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' -MaxEvents 50 -ErrorAction SilentlyContinue | Where-Object { $_.Id -in 100, 200, 201 -and $_.TimeCreated -gt \\(Get-Date\\).AddMinutes\\(-30\\) } | Select-Object TimeCreated, Id, @{N='Task';E={$_.Properties[0].Value}}, @{N='Detail';E={$_.Properties[1].Value}} | Format-Table -AutoSize -Wrap)",
-      "PowerShell(Get-CimInstance *)",
-      "Bash(pm2-broker list *)",
-      "Bash(cygpath -w /tmp/cc-leveldb-probe)",
-      "Read(//c/Users/<user>/.claude/skills/github-ops/**)",
-      "Read(//c/Users/<user>/.claude/skills/github-ops/references/**)",
-      "Bash(cp \"C:/Projects/claude-mods/skills/github-ops/SKILL.md\" ~/.claude/skills/github-ops/SKILL.md)",
-      "Bash(cp \"C:/Projects/claude-mods/skills/github-ops/references/readme-description.md\" ~/.claude/skills/github-ops/references/readme-description.md)",
-      "Bash(cp \"C:/Projects/claude-mods/skills/github-ops/references/metadata-checklist.md\" ~/.claude/skills/github-ops/references/metadata-checklist.md)",
-      "Bash(pigeon read *)",
-      "Bash(/c/Users/<user>/AppData/Local/Programs/Python/Python313/python -)"
-    ],
-    "deny": [],
-    "ask": [
-      "Bash(rm -rf:*)",
-      "Bash(git reset --hard:*)",
-      "Bash(git clean -f:*)",
-      "Bash(git push --force:*)",
-      "Bash(git push -f:*)",
-      "Bash(git branch -D:*)"
-    ]
-  },
-  "hooks": {},
-  "outputStyle": "Vesper"
-}

+ 3 - 0
.gitignore

@@ -5,6 +5,9 @@
 # Context init file (regenerated each session)
 .claude/.context-init.md
 
+# Local settings (user-specific; may contain API keys in permission rules)
+.claude/settings.local.json
+
 # Session state (user-specific)
 .claude/session-cache.json
 .claude/claude-state.json

+ 22 - 0
skills/setperms/SKILL.md

@@ -59,6 +59,7 @@ Tools from [dev-shell-tools](https://github.com/0xDarkMatter/dev-shell-tools):
     +-- Create .claude/rules directory
     |
     +-- Write settings.local.json (permissions)
+    +-- Ensure .gitignore excludes settings.local.json (secret-safety)
     +-- Write rules/cli-tools.md (tool preferences)
 ```
 
@@ -183,6 +184,27 @@ Write to `.claude/settings.local.json`:
 }
 ```
 
+### Step 3b: Protect settings.local.json from git (mandatory)
+
+`settings.local.json` is user-specific and frequently accumulates secrets in
+permission rules (an API key baked into a `Bash(...)` allow entry, a token in a
+custom command). It must never reach a remote. Before finishing, ensure the repo
+root `.gitignore` excludes it:
+
+```bash
+# Add the rule only if it's not already present
+grep -qxF '.claude/settings.local.json' .gitignore 2>/dev/null \
+  || printf '\n# Local settings (user-specific; may contain API keys)\n.claude/settings.local.json\n' >> .gitignore
+
+# If it was already tracked from a prior commit, stop tracking it (keeps the file)
+git ls-files --error-unmatch .claude/settings.local.json >/dev/null 2>&1 \
+  && git rm --cached .claude/settings.local.json
+```
+
+Skip silently if the project has no git repo. This pairs with the git-ops
+push-safety gate, which also refuses any push that adds `.claude/settings.local.json`
+— defense in depth so a leaked key can't recur.
+
 ### Step 4: Write rules file
 
 Write to `.claude/rules/cli-tools.md`: