DEV Community

Cover image for Claude Code 2.1.214: Audit Your Agent Permissions Before They Auto-Approve
Zira
Zira

Posted on

Claude Code 2.1.214: Audit Your Agent Permissions Before They Auto-Approve

A coding agent is only as safe as the rules that decide which tool calls need a human. In Claude Code 2.1.214, Anthropic tightened several permission checks after finding cases where path patterns, shell parsing, or remote-session timing could approve more than intended. The release is not a reason to panic. It is a useful reminder to treat your agent permissions like production policy, not personal convenience.

This article turns the release notes into a small audit you can run on a real repository. The focus is Claude Code permissions, with practical checks for allow rules, shell commands, hooks, and telemetry.

What changed in Claude Code 2.1.214

The official 2.1.214 release notes list several hardening fixes:

  • A single-segment dir/** allow rule such as Edit(src/**) no longer auto-approves writes to matching nested directories anywhere in the tree. It is limited to the intended cwd/dir scope.
  • Windows PowerShell 5.1 permission checks now fail closed in a bypass case.
  • Bash file-descriptor redirect forms, long commands over 10,000 characters, zsh variable subscripts, and some help and man invocations now prompt instead of being treated as safe.
  • Remote-session permission prompts no longer proceed before the local confirmation dialog.
  • OpenTelemetry events now include message.uuid, client_request_id, and tool_source for message-level correlation and tool provenance.
  • Long-running tool calls get a periodic progress heartbeat instead of appearing silent.

The follow-up 2.1.215 release also changes review behavior: Claude no longer runs the /verify and /code-review skills on its own. Invoke them explicitly when you want them.

The common thread is control-plane clarity: a tool call should be scoped, observable, and explicit about when review happens.

Why your old allow rules deserve a review

Claude Code's permission documentation says rules are evaluated in deny, ask, allow order. A broad deny still wins over a narrower allow. That is good, but it does not make a broad allow safe by itself.

The risky pattern is usually not a dramatic rm -rf rule. It is an apparently harmless shortcut that grew over time:

{
  "permissions": {
    "allow": [
      "`Edit(src/**)`",
      "`Bash(npm run *)`",
      "`Bash(git *)`"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

The first rule may cover more paths than your team intended if you have not checked its anchor and depth. The second may allow scripts whose behavior changes with package configuration. The third deserves special scrutiny because Git commands can change branches, run hooks, rewrite state, or move data outside the task's narrow goal.

A useful policy question is not “Does this usually work?” It is “What is the widest action this pattern can approve after a repository, shell, or working-directory change?”

A 10-minute Claude Code permission audit

1. Inventory every rule and its source

Run /permissions inside a session. The UI shows each rule and the settings file it came from. Review all five scopes: managed settings, command-line overrides, local project settings, shared project settings, and user settings.

Export or inspect the project files directly when reviewing a pull request:

find .claude -maxdepth 2 -type f -name '`settings*.json`' -print
cat .claude/settings.json
cat .claude/settings.local.json 2>/dev/null || true
Enter fullscreen mode Exit fullscreen mode

Look for rules that are broader than the task requires, anchored to the wrong directory, based on a command prefix that can invoke scripts or hooks, duplicated across user and project scopes, or missing an explicit deny or ask rule for sensitive paths.

2. Replace convenience globs with task-shaped rules

Prefer a narrow allowlist for routine work:

{
  "permissions": {
    "allow": [
      "Bash(npm run test)",
      "Bash(npm run lint)",
      "Bash(git status)",
      "`Bash(git diff *)`",
      "`Edit(/src/**)`"
    ],
    "ask": [
      "`Bash(git commit *)`",
      "`Bash(git push *)`",
      "`Edit(/migrations/**)`"
    ],
    "deny": [
      "`Read(//**/.env)`",
      "`Edit(/.github/workflows/**)`"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

The leading slash in Edit(/src/**) anchors the rule to the project root when it is defined in project settings. An absolute filesystem path needs the double-slash form, such as Read(//Users/alice/secrets/**). Verify the anchor instead of assuming the visual shape of the glob explains its scope.

3. Test the negative cases

Do not only test that the agent can run the happy-path command. Create a disposable branch or container and check that these cases still prompt or deny:

  • a command with a redirect or a second subcommand
  • a very long generated shell command
  • a symlink from an allowed directory to a secret outside it
  • a path in a sibling directory with a similar name
  • a PowerShell command when Windows is part of your support matrix
  • a remote session where a local approval is required

A permission rule is a security boundary only when the negative cases are part of the test plan.

Add observability without logging secrets

2.1.214's OpenTelemetry fields make it easier to correlate a user message with tool activity. Use that correlation to answer operational questions:

trace_id
message.uuid
client_request_id
tool_source
tool_name
permission_decision
repository_id
latency_ms
result_status
Enter fullscreen mode Exit fullscreen mode

Keep the payload minimal. Do not export raw prompts, file contents, credentials, or full shell commands by default. If you enable content attributes, review the OpenTelemetry content-length setting and your retention policy first.

Alert on patterns rather than sensitive content: an allow rule used outside its expected repository, repeated permission denials followed by a broader rule, a tool call that waits unusually long before approval, a background or remote call whose local confirmation is missing, or a sudden increase in shell commands per task.

The Claude Code hooks reference is useful for local enforcement and event-driven audit records. Hooks can block a tool call before it runs, but they should complement permission rules rather than replace deny-first policy.

Explicit review is safer than invisible review

In 2.1.215, /verify and /code-review are no longer invoked automatically. That behavior change is easy to misread as less safety. I see it differently: it makes review a visible workflow step.

For a pull request, use a deliberate sequence:

  1. Ask the agent to implement the change.
  2. Inspect the diff and test output.
  3. Run /verify explicitly.
  4. Run /code-review explicitly with the acceptance criteria.
  5. Require a human decision before commit, push, or merge.

This avoids a false sense of safety where a review skill ran somewhere in the session but nobody knows which files, tests, or policy it actually covered.

What to do now

  1. Upgrade a disposable project to Claude Code 2.1.214 or newer and read the release notes for your installed version.
  2. Run /permissions and inventory every allow, ask, and deny rule with its source.
  3. Replace broad command and path globs with task-shaped rules.
  4. Test redirects, symlinks, long commands, subdirectories, and remote approval flows.
  5. Add message-to-tool correlation fields to your telemetry while excluding secrets and raw content.
  6. Make /verify and /code-review explicit checklist steps in your pull-request workflow.
  7. Revisit the policy after one week of traces. Tighten rules that were never needed; split rules that produce too many prompts.

This is not a benchmark, and the release does not prove that every agent workflow is secure. Permissions remain one layer. Use sandboxing, protected branches, secret scanning, and human review for high-impact changes.

Sources

What is the most useful negative-case test in your coding-agent permission suite: symlinks, shell redirects, remote approvals, or something else?

Top comments (0)