Skip to content

chore: auto-approve read-only Bash and tools in shared Claude Code config#4174

Merged
iancooper merged 1 commit into
masterfrom
chore/claude-readonly-permissions
Jun 9, 2026
Merged

chore: auto-approve read-only Bash and tools in shared Claude Code config#4174
iancooper merged 1 commit into
masterfrom
chore/claude-readonly-permissions

Conversation

@iancooper

Copy link
Copy Markdown
Member

What

Adds a permissions.allow list to the team-shared .claude/settings.json so read-only operations no longer prompt for confirmation when using Claude Code.

Why

Reduce friction for the whole team — read-only commands are safe to auto-approve, and keeping this in the version-controlled project config means everyone shares the same baseline.

What's auto-approved

  • Built-in tools: Read, Glob, Grep
  • Read-only Bash: ls, cat, head, tail, wc, pwd, echo, which, find, grep, rg, tree, file, stat, diff
  • Read-only git: status, log, diff, show, branch, remote, tag, blame, describe, rev-parse, ls-files, config --get
  • Read-only gh: pr view/list/diff, issue view/list
  • dotnet info: --version, --info, --list-sdks, --list-runtimes

What's deliberately excluded

  • Existing deny rules (curl, wget, ssh, secret-file reads, WebFetch) are untouched. Deny takes precedence over allow, so those stay blocked.
  • Bash entries are scoped to specific command prefixes rather than a blanket Bash allow, so writes/deletes still prompt.
  • Build/test commands (dotnet build/test/restore) are not included since they execute code and aren't strictly read-only.

Individuals can override locally via the gitignored .claude/settings.local.json.

🤖 Generated with Claude Code

…nfig

Add a permissions.allow list to .claude/settings.json so read-only
operations (Read/Glob/Grep, common read-only Bash commands, read-only
git/gh queries, and dotnet info commands) no longer prompt for
confirmation. Existing deny rules (curl/wget/ssh, secret reads,
WebFetch) are unchanged and still take precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the Maintenance Build, CI, refactoring, testing infrastructure, and other chores label Jun 9, 2026
@iancooper iancooper merged commit 5a31232 into master Jun 9, 2026
7 of 8 checks passed
@iancooper iancooper deleted the chore/claude-readonly-permissions branch June 9, 2026 14:35

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our agent can fix these. Install it.

No application code in the PR — skipped Code Health checks.

Quality Gate Profile: Clean Code Collective
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@claude

claude Bot commented Jun 9, 2026

Copy link
Copy Markdown

Claude Code Review

Thanks for this — it's a focused, well-documented config change, and the PR description does a nice job laying out what's included and (importantly) what's deliberately excluded. Scope is clean: one file, additive only, no behavioral code touched. A few observations, mostly around the security model since that's the crux of this change.

🔐 Read-only Bash commands can bypass the Read() secret-deny rules

This is the one I'd push back on. The existing deny block protects secrets only at the Read tool layer:

"deny": [
  "Read(./.env*)", "Read(./secrets/**)", "Read(./**/credentials*)",
  "Read(./**/*.key)", "Read(./**/*.pem)", ...
]

But the new allow list auto-approves shell readers that can dump those exact files without ever invoking the Read tool:

cat .env
head secrets/prod.json
tail -n 100 deploy.key
grep -r AWS_SECRET .
stat .env && file id_rsa.pem
diff .env .env.bak

All of these now match Bash(cat:*) / Bash(head:*) / Bash(grep:*) / etc. and run without a prompt, defeating the intent of the Read(...) deny rules. deny takes precedence over allow, but only for the same tool — there is no Bash(...) deny covering secret paths, so the protection has a gap. Worth adding matching Bash deny entries, e.g.:

"deny": [
  "Bash(cat:*.env*)", "Bash(cat:*.key)", "Bash(cat:*.pem)",
  "Bash(cat:*secrets/*)", "Bash(cat:*credentials*)"
  // ...and the same for head/tail/grep/find/stat/file/diff
]

(Path-pattern matching for Bash(...) is coarse, so this won't be airtight, but it closes the obvious holes. At minimum I'd call out in the PR that the secret-deny rules are Read-tool-only.)

⚠️ Bash(find:*) is not strictly read-only

find accepts actions that mutate or execute:

find . -name '*.tmp' -delete
find . -type f -exec rm {} \;

Both match the Bash(find:*) prefix and would be auto-approved. The PR's stated principle is "read-only commands are safe to auto-approve" — find violates that. Consider dropping it from allow (it will still work, just with a prompt), or accept the tradeoff explicitly. echo has a milder version of the same issue (echo x > file writes), though shell-redirection handling makes that less clear-cut.

Minor

  • git log / git show can be steered to write output via --output=<file>; low risk and probably not worth guarding, just noting these prefixes are not 100% side-effect-free either.
  • The PR description claim that individuals can override locally via the gitignored .claude/settings.local.json checks out — .gitignore does list it. 👍

Test coverage / performance

N/A — config-only change, no runtime impact.

Verdict

Direction is good and the friction reduction is worthwhile. I'd recommend either adding Bash-layer deny rules for the secret paths and dropping find from the allow list, or — if you'd rather keep the list lean — documenting that the auto-approved shell readers intentionally do not honor the Read() secret denials so the team understands the boundary.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Maintenance Build, CI, refactoring, testing infrastructure, and other chores

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant