Skip to content

tools.exclude fails to block specific shell commands #17728

Description

@rossdonald

What happened?

When using tools.exclude in settings.json to block specific shell commands e.g., "run_shell_command(ls)", the CLI still prompts the user for approval instead of blocking the command immediately. This occurs because the exclusion logic treats the entire string as a literal tool name and does not support the toolName(args) syntax or normalization of the ShellTool alias.

Problem Area

The issue was located in packages/core/src/policy/config.ts:216, where tools.exclude is processed without the same parsing logic that is used in tools.allowed:

for (const tool of settings.tools.exclude) {
rules.push({
toolName: tool,
decision: PolicyDecision.DENY,
priority: 2.4,
source: 'Settings (Tools Excluded)',
});
}

The logic for allowed tools with parsing and normalisation:

for (const tool of settings.tools.allowed) {
// Check for legacy format: toolName(args)
const match = tool.match(/^([a-zA-Z0-9_-]+)\((.*)\)$/);
if (match) {
const [, rawToolName, args] = match;
// Normalize shell tool aliases
const toolName = SHELL_TOOL_NAMES.includes(rawToolName)
? SHELL_TOOL_NAME
: rawToolName;

What did you expect to happen?

It should say "Tool execution denied by policy." rather than prompting.

Entries in tools.exclude should support the same syntax as tools.allowed. Specifically:

  1. run_shell_command(cmd) should block only that specific command.
  2. ShellTool should be normalized to run_shell_command.
  3. Blocked tools should fail immediately without prompting the user.

Client information

Client Information

Run gemini to enter the interactive CLI, then run the /about command.

> /about
│ CLI Version                              0.25.2                                                                      │
│ Git Commit                               18e854c33                                                                   │
│ Model                                    gemini-3-flash-preview                                                      │
│ Sandbox                                  no sandbox                                                                  │
│ OS                                       win32                                                                       │
│ Auth Method                              OAuth 

Login information

Google Account

Anything else we need to know?

My tools config in settings.json

{
  "tools": {
    "allowed": [
      "run_shell_command(git diff)",
      "run_shell_command(git status)"
    ],
    "exclude": [
      "run_shell_command(ls)",
      "run_shell_command(git add)",
      "run_shell_command(git commit)",
      "run_shell_command(git push)"
    ]
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Stalearea/enterpriseIssues related to Telemetry, Policy, Quota / Licensingstatus/need-triageIssues that need to be triaged by the triage automation.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions