2.1.150 forces users into `dangerouslyDisableSandbox: true` for routine git-hook installs — security posture own-goal

Resolved 💬 3 comments Opened May 23, 2026 by spectre6000 Closed Jun 24, 2026

_Companion to #61909 (the polite, technical version). This one says what #61909 should have led with: the user-pain framing the polite version sanitizes._

The actual problem

Claude Code 2.1.150 added a sandbox deny on hooks/ and .git/hooks/ with no documented way to opt out. The only way past — verified by exhausting every documented setting knob — is dangerouslyDisableSandbox: true on every affected command.

This is the same flag whose own name says "dangerous," whose help text says "Recommended only for sandboxes with no internet access," and which users are explicitly trained by the product to avoid. The product simultaneously:

  • Ships UI/messaging telling users to treat --dangerously-skip-permissions and dangerouslyDisableSandbox: true as last resorts.
  • Ships a deny rule with no documented narrow opt-out.
  • Provides no other path that reaches the same end state.
  • ...for a workflow that has worked unmodified for over a year (installing project-committed git hooks via make install-hooks or equivalent — a pattern every git project of any size uses).

So users who want to keep using their existing workflow are FORCED into the very flag the product warns them not to use. That's not a security improvement; it's a regression in security posture, because it normalizes the dangerous flag in users' muscle memory.

How this lands in practice

Real session from today, agent attempting to install a project-shipped pre-push hook on the user's authorization:

  1. cp hooks/pre-push .git/hooks/pre-push → "Operation not permitted"
  2. Add .git/hooks to sandbox.filesystem.allowWrite in .claude/settings.local.json → no effect (allowWrite is additive-merge per docs; doesn't override denyWithinAllow)
  3. Read the entire sandbox config schema docs → no override key documented
  4. grep the binary for undocumented override knobs → none
  5. Settings sandbox.enabled: false → works but is session-wide blanket disable (worse than per-command bypass)
  6. Downgrade to 2.1.148 → works but loses other 2.1.150 improvements
  7. dangerouslyDisableSandbox: true per-command → the only documented narrow path. This is the flag the product tells you to avoid.

The user spent the better part of an hour exhausting documented options before accepting they had to use the dangerous flag for what used to be cp hooks/pre-push .git/hooks/pre-push. The fix:

"anthropic FORCING me into dangerous disabling is pretty fucking stupid"

Verbatim from the user authorizing this issue. Putting it in scare-quote-free here so it can't be mistaken for sanitized paraphrase. The frustration is justified — the user has done the homework, the documented config surface doesn't have what's needed, and the only escape hatch is a flag they've been (correctly) trained to refuse.

Why "ship strict by default, opt-out via config" is the standard pattern

Most sandbox-style protections in mature tools follow: ship the strict default, expose a granular opt-out for legitimate per-project use cases. Examples:

  • macOS tccutil + Privacy & Security panel — strict app-permission defaults, per-app explicit grant.
  • iOS app sandboxing — strict file/network defaults, entitlements declare opt-in capabilities.
  • GitHub Actions permissions — restrictive default for GITHUB_TOKEN, per-workflow permissions: block widens.
  • Docker — restrictive container default, --cap-add / --security-opt for specific carve-outs.

In every one of those, the protection is "deny by default, allow specific narrow exceptions via documented config." Claude Code 2.1.150's hooks/ deny is "deny, period, with no documented narrow exception." That's the missing half of the pattern.

The security theater critique: a deny rule that pushes users to a wider, more dangerous flag is worse for the security posture than no deny at all. Users would have run their narrow cp operation before the deny; now they run dangerouslyDisableSandbox: true (which disables the sandbox for the WHOLE command, not just the hooks-related operations). The deny made the attack surface bigger for legitimate users, not smaller.

What needs to change

Either of:

  1. Granular opt-out config — the trustedHookSources (or allowWriteWithinDeny) proposal in #61909. Lets users opt specific paths back in without removing the protection wholesale.
  2. Documented allowed alternative — if the deny is intentional and there's a different supported way to install hooks (e.g., a built-in claude hooks install command), document it prominently. Right now the docs and the binary's CLI help mention nothing of the kind.

Either fixes the "users forced to dangerous bypass" problem. Neither weakens the protection against malicious hook implantation (#61909 explains why).

Meta-observation about the product

The same 2.1.150 release also added:

  • An auto-mode classifier that fires "Self-Modification" denials on edits to .claude/settings*.json — the very file the user needs to edit to grant the agent any additional permissions. Without a separate global allow rule in ~/.claude/settings.json, the agent can't fix its own sandbox config, even when the user explicitly tells it to. Chicken-and-egg.
  • Network restrictions that break gh CLI (TLS cert verify on api.github.com fails despite allowedDomains setting) and Chromium launch (Mach-port registration denied despite allowMachLookup setting). Both blocking common agent workflows. Both with no documented setting to fix.
  • See related issue #61909 for the technical detail on these.

The pattern across all of these: strict new defaults shipped without the corresponding granular-opt-out config surface. Each one individually is a "small inconvenience." Together they force the user into either downgrade, blanket sandbox disable, or the dangerous-bypass flag — every time. The cumulative effect is that legitimate users are systematically pushed toward the wider-blast-radius escape hatches, which is the opposite of the safety goal.

Version

Claude Code 2.1.150 on macOS 26.3 (Darwin 25.3.0), Apple Silicon. Auto-update on a homebrew-installed copy.

Cross-reference

Polite version of the same problem: #61909.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗