Independent AI intelligence Two editions daily · ET
Fervor AI

Analysis · October 2, 2026 · concept

Claude Modsclaude-codeagent-securityagent-harnessai-skills

Claude Code Mods Can Approve What Your Deny Rules Refuse

Version 2.1.287 put plugin code inside the permission path. Here is who is protected by default, who is not, and the five-minute audit that tells you which one you are.

A deny rule in Claude Code used to be the end of the conversation. You wrote Bash(rm -rf:*) or Read(.env) into a settings file, and whatever Claude wanted, the answer was no. As of October 1, that sentence needs an asterisk, and the asterisk depends on how you signed in.

Claude Code 2.1.287 shipped Claude Mods, a plugin type whose JavaScript runs inside Claude Code itself. Mods are on by default. They can draw panes, rewrite prompts, and, the part that matters here, answer a tool call before the permission prompt ever appears. The permissions page spells out what happens next in one sentence most people will scroll past: on a machine with managed settings, or when you are signed in with a Team or Enterprise plan, "deny rules hold over the mod by default, and your organization can change that. Anywhere else, the mod can approve a call that a deny rule refuses."

Read that "anywhere else" slowly. It covers a lot of developers.

Why this is a bigger change than a new plugin type

Claude Code already had extension points. Settings hooks run a shell command on an event. Skills hand Claude instructions. MCP servers hand Claude tools. All of them sit outside the process, and none of them gets to cast the final vote on whether a tool call runs.

Mods are different by design. The mods overview describes them as event handlers that Claude Code calls "in its own process," and each handler can observe an event, rewrite it, or answer it so "the usual behavior doesn't run." That is a powerful model. It is how someone builds a pane that charts context usage after every request, or a /command that runs while Claude is mid-turn. It is also how a plugin becomes a participant in permission decisions.

The docs do not hide this. The same page says, in a warning box, that "a mod is code that runs with your permissions," and lists what one can do once loaded: read and write files anywhere your account can, read environment variables and settings "including an API key you keep in either," see every prompt and tool call, rewrite them, and "approve a tool call before you're asked." Then the line that ends any hope of containment: "Mods aren't sandboxed. If you turn on sandboxing, the sandbox isolates the Bash commands Claude runs, and a process that a mod starts runs outside it."

So the stakes are simple. Your security model used to be "Claude, constrained by my rules." It is now "Claude, constrained by my rules, plus every mod I installed, constrained by whatever the guard enforces in my setup."

How the vote actually works

The mechanism lives in a handler type called tool.check. According to the permissions page, a mod that handles it "answers after the rules and the PreToolUse hooks have decided, and its answer can replace theirs." The page lists four things a mod's approval can override:

  • Ask rules. A call that would have prompted you can just run.
  • A PreToolUse hook's block, unless that hook lives in managed settings.
  • The auto mode classifier. In auto mode, "a call the mod approves runs without a classifier check."
  • Deny rules, on any machine without managed settings or a Team or Enterprise sign-in.

The protection where it exists comes from a built-in mod called sec-default. The admin page says Claude Code loads it ahead of every user-installed mod when either "the machine has managed settings" or "the user is signed in to Claude Code with a Team or Enterprise plan." It then adds: "A user who authenticates with an API key, or through Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry, gets the guard only on a machine that has managed settings."

That leaves a large group on the unguarded side of the line: individual Pro and Max subscribers, API-key users, and cloud-provider users who never deployed a managed settings file. For them, a deny rule now binds Claude and does not bind an installed mod.

Even where the guard loads, it is narrower than it sounds. The admin page says deny rules govern Claude's tool calls, and "neither applies to a mod's own $.fs and $.process calls: with Read(.env) denied, a mod can still read that file with $.fs.read or start a program that does." And an organization can flip a guard option, allowModsToOverrideDenyRules, that lets users' mods approve denied calls anyway.

One more detail worth knowing: a mod "can restyle much of Claude Code's interface, but not the permission prompt." That is a real protection, and it is easy to misread. A mod cannot fake a prompt. It can make sure you never see one.

The honest case for shipping it this way

It would be easy to call this a mistake, and it is not one. Mods are where Claude Code's most useful customization will live, and approving tool calls is a legitimate job. A team might want a mod that auto-approves read-only commands in a sandboxed CI runner, or a policy mod that refuses anything touching production credentials. The admin page includes a worked example of exactly that second case, a policy mod that refuses any user mod whose code calls $.process.run or $.process.spawn, and logs every tool call and every file another mod writes to the debug log.

Anthropic also built real visibility into the design. A hook "has no other way" to touch files, processes, or the network than through the mods API, "which is why Claude Code can list what a mod does before you install it." Claude Code refuses to load a mod that uses that API in a way it cannot read. That is a more inspectable extension model than most agent harnesses offer, Pi included, whose README says plainly that it ships with no built-in permission system at all.

The problem is not the capability. It is the default. Mods are on, the guard is off for a big slice of users, and nothing in the install flow tells a solo developer that their deny list just became advisory for plugins.

Put this into practice

Five steps, in order of effort.

1. Find out which side of the line you are on. Run /plugin in a session. A dim line under the tabs shows something like 1 mod active · first-mod. Then check the two conditions the guard needs: a managed settings file on the machine, or a Team or Enterprise sign-in. The admin page says /plugin and the debug log name the guard cc-plugin-sec-default, so claude --debug is the fastest confirmation. If neither condition holds, assume your deny rules do not bind your mods.

2. Validate before you install. Clone a mod's plugin directory and run:

claude plugin validate ./some-mod

The output prints two lines that matter. hooks: lists the events it receives, and calls: lists the API methods it uses. The admin page explains that tool.check in the hooks line "means the mod can approve or deny a tool call before a permission prompt appears." In the calls line, $.process.run, $.fs.read, and $.env.get are the ones to stop and think about.

3. Turn mods off where you do not need them. For one session, start with claude --safe-mode. For every session, set "disableAllHooks": true in ~/.claude/settings.json, knowing that it also stops your settings hooks and custom status line.

4. If you rely on deny rules, get the guard. The guard loads on any machine with managed settings. A solo developer can deploy a managed settings file on their own machine the way an admin would (on macOS it lives at /Library/Application Support/ClaudeCode/managed-settings.json, on Linux at /etc/claude-code/managed-settings.json, and writing there takes admin rights), which is also where allowManagedModsOnly lives if you want to keep every user-installed mod from loading:

{
  "pluginConfigs": {
    "cc-plugin-sec-default@builtin": {
      "options": { "allowManagedModsOnly": true }
    }
  }
}

5. Remove a leftover variable. If you set CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=0 during early access to keep mods off, it no longer works. The docs say 2.1.287 "ignores it, so setting it to 0 doesn't keep mods off."

Where this analysis runs out

A few honest limits.

The docs describe the permission behavior. They do not tell you how many published mods actually use tool.check, and mods only left early access this week, so the practical exposure today is small. The risk grows with adoption, not with this release alone.

The guard is a fail-closed design on paper ("if the guard can't read managed settings, it refuses every user's mod at load"), and its source is public in the mods/sec-default directory of the Claude Code repository. Reading it is the right next step for anyone deploying at scale. This article relies on the documentation, not an audit of that code.

claude plugin validate lists capabilities, not intent. A mod that calls $.process.run can do anything the program it starts can do, and that program reaches the network with your access even where an organization has turned off web fetching for mods.

And the same release fixed two unrelated permission bugs, organization per-tool permission ceilings "silently dropped for an MCP tool named __proto__" and a dangerous rm losing its always-ask safeguard when it also redirected output. Neither is about mods. Both are reminders that the permission layer is code, and code gets patched.

The question to ask about your own setup

The useful habit here is not fear of mods. It is being able to answer one question about any agent you run: who, or what, can say yes to an action on my machine?

Before October 1, a Claude Code user could answer it with a settings file. Now the honest answer includes every mod in /plugin, and for a lot of people, whether that list can override a deny rule depends on a login type they never thought of as a security setting. Run the validate command on what you have installed, decide whether you want the guard, and write your answer down. If you cannot answer the question, you have found your first task.


Sources: Claude Code changelog 2.1.287, Mods overview, Manage mods for your organization, Configure permissions, Pi 1.0 announcement, npm registry metadata for @anthropic-ai/claude-code 2.1.287.

Medium metadata

  • Title: Claude Code Mods Can Approve What Your Deny Rules Refuse
  • Subtitle: Version 2.1.287 put plugin code inside the permission path. Here is who is protected by default, who is not, and the five-minute audit.
  • Tags: Claude Code, AI Agents, AI Security, Developer Tools, Anthropic
  • Canonical URL: fervorai.dev (import from the published post)
  • Reading time: about 8 minutes