Paths Subjects Questions Quizzes Pricing Search
Intermediate Open Free

Scoping settings.json for a New Contractor

A contractor is joining your team for two weeks to fix bugs in a Node.js service. They'll use Claude Code daily. Someone on the team suggests: "just add Bash(*) to the allowlist so they're not constantly clicking through prompts — it's only two weeks."

  1. Explain concretely what Bash(*) allows that the team probably didn't intend, using the actual matching behavior of permission rules.
  2. Propose a narrower permissions block (allow and deny) that removes most of the prompt fatigue for routine bug-fixing work without granting blanket trust.
  3. Name one additional layer, beyond settings.json permissions, that would catch a mistake even if the allowlist above were somehow too permissive.
Solution

1. What Bash(*) actually grants:

Permission rules match on the shell command's shape (a name plus a prefix pattern), not on the intent behind it. Bash(*) matches every possible Bash invocation — npm test and npm run lint, yes, but also rm -rf, curl | bash, git push --force, and anything else the agent might construct as a shell command in the course of a task. The team wanted to stop approving routine test/lint commands; what they actually did was remove the one human checkpoint on every irreversible or externally-visible action a contractor's session could take, intentionally or by mistake.

2. A narrower permissions block:

{
  "permissions": {
    "allow": [
      "Bash(npm run test *)",
      "Bash(npm run lint)",
      "Bash(npm run build)",
      "Bash(git diff *)",
      "Bash(git status *)",
      "Bash(git log *)"
    ],
    "deny": [
      "Bash(git push --force *)",
      "Bash(rm -rf *)",
      "Bash(curl *)",
      "Read(./.env)",
      "Read(./secrets/**)"
    ]
  }
}

This allows exactly the read-only and routine build/test commands a bug-fixing task needs without a prompt each time, explicitly denies the destructive/network-reaching categories regardless of how a task is phrased, and leaves everything else — including commands nobody anticipated — falling back to an interactive prompt. That's the narrow-allow, explicit-deny pattern: scoped to trusted commands, not to the tool category that happens to contain them.

3. An additional layer:

A PreToolUse hook (or the sandbox) as a deterministic backstop — for example, a hook that blocks any Bash command containing --force or rm -rf regardless of what's in permissions.allow. Unlike the allowlist, which is a static pattern match set up once, a hook runs a real check on every call and isn't relying on someone having anticipated every dangerous pattern in advance. Sandboxing is the other option: it restricts what sandboxed commands can touch at the OS level (filesystem and network), independent of what permission rules say.

Share this question

← Back to Claude Code Best Practices: Reliable, Cheap, High-Leverage Sessions practice

We use cookies for product analytics to improve OmniAtlas. See our Privacy Policy.