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:
run_shell_command(cmd) should block only that specific command.
ShellTool should be normalized to run_shell_command.
- 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)"
]
}
}
What happened?
When using
tools.excludeinsettings.jsonto 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 thetoolName(args)syntax or normalization of theShellToolalias.Problem Area
The issue was located in
packages/core/src/policy/config.ts:216, wheretools.excludeis processed without the same parsing logic that is used intools.allowed:gemini-cli/packages/core/src/policy/config.ts
Lines 215 to 222 in 2c09785
The logic for allowed tools with parsing and normalisation:
gemini-cli/packages/core/src/policy/config.ts
Lines 228 to 236 in 2c09785
What did you expect to happen?
It should say "Tool execution denied by policy." rather than prompting.
Entries in
tools.excludeshould support the same syntax astools.allowed. Specifically:run_shell_command(cmd)should block only that specific command.ShellToolshould be normalized torun_shell_command.Client information
Client Information
Run
geminito enter the interactive CLI, then run the/aboutcommand.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)" ] } }