Skip to content

Extension docs recommend an excludeTools form that is never matched #28962

Description

@chandlerm923

What happened?

docs/extensions/best-practices.md recommends this manifest as the way to keep a
powerful tool in check:

{
  "name": "my-safe-extension",
  "excludeTools": ["run_shell_command(rm -rf *)"]
}

This ensures the CLI blocks dangerous commands even if the model attempts to
execute them.

docs/extensions/reference.md:162-166 gives the same guidance, and the repo
ships an example manifest using the same shape:

// packages/cli/src/commands/extensions/examples/exclude-tools/gemini-extension.json
{
  "name": "excludeTools",
  "version": "1.0.0",
  "excludeTools": ["run_shell_command(rm -rf)"]
}

Extension excludeTools entries are matched by exact string equality, so an
entry containing (...) never matches a tool and nothing is excluded.
Config.getExcludeTools() collects the entries into a Set
(packages/core/src/config/config.ts:2431-2439) and ToolRegistry tests
membership directly (packages/core/src/tools/tool-registry.ts:637):

return !possibleNames.some((name) => excludeTools?.has(name));

possibleNames holds the tool's real names (run_shell_command, its class
name, and MCP-qualified variants), none of which equal
"run_shell_command(rm -rf *)".

How can this be reproduced?

Applying that matching to the documented manifests:

excludeTools: ["run_shell_command(rm -rf)"]     -> run_shell_command still active
excludeTools: ["run_shell_command(rm -rf *)"]   -> run_shell_command still active
excludeTools: ["run_shell_command"]             -> run_shell_command excluded

Only the bare tool name has any effect. The documented form silently does
nothing — no warning is emitted that the entry was ignored.

Why this is a docs problem rather than a code one

The parenthesised toolName(args) syntax is real, but it belongs to a different
setting. tools.core and tools.allowed are parsed by mapToolsToRules
(packages/core/src/policy/config.ts:445-476), which splits the shape into a
tool name plus an args pattern. Extension excludeTools does not go through
that path, and tools.exclude — the settings-level equivalent — is already
deprecated in favour of the policy engine (#18508), which is documented in
docs/tools/shell.md:158 and docs/cli/enterprise.md:267.

For the "block one specific command" use case the extension docs are describing,
the policy engine already supports it directly, and extensions can ship policies
in a policies/ directory (docs/reference/policy-engine.md:143,
packages/cli/src/config/extension-manager.ts:932):

toolName = "run_shell_command"
commandPrefix = "rm -rf"
decision = "deny"
priority = 100

So the extension docs appear to have been missed when that guidance moved to the
policy engine.


What did you expect to happen?

The extension documentation and the shipped example should show a form that
actually takes effect — either the bare tool name, or a policy rule for
command-level blocking — rather than a form that is silently ignored while the
text promises it "blocks dangerous commands".

A documentation PR is linked below.


Client information

Client Information
> /about
About Gemini CLI
CLI Version    0.56.0-nightly.20260806.g761f604c1
Git Commit     5411f113c
Model          gemini-3-pro-preview
Sandbox        no sandbox
OS             darwin

Platform: macOS. The matching path is platform independent — it compares
configuration strings only.


Login information

Not applicable. This concerns tool filtering configuration, independent of the
authentication method.


Anything else we need to know?

Severity. This is not an exploit path and needs no attacker. A user who
copies the documented snippet ends up without the exclusion they think they
configured; run_shell_command then follows the default write policy, which
still prompts for confirmation rather than running silently. The issue is the
gap between what the docs promise and what the configuration does.

Related. #17728 reported the same exact-match behaviour for the
settings-level tools.exclude and was closed as stale; that setting has since
been deprecated by #18508. Extension excludeTools carries no deprecation
marker and is still the documented mechanism, which is why the docs matter here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/agentIssues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Qualitykind/bugpriority/p2Important but can be addressed in a future release.status/bot-triaged

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions