Skip to content

Security Fix: Prevent HITL Bypass via UI Truncation (Newline Injection) in Command Confirmations - #23333

Closed
likhith-adithya wants to merge 9 commits into
google-gemini:mainfrom
likhith-adithya:fix-HITL-Bypass-via-UI-Truncation
Closed

likhith-adithya wants to merge 9 commits into
google-gemini:mainfrom
likhith-adithya:fix-HITL-Bypass-via-UI-Truncation

Conversation

@likhith-adithya

@likhith-adithya likhith-adithya commented Mar 21, 2026 •

Copy link
Copy Markdown

Fixes #23433

Summary

Fixes a critical Human-in-the-Loop (HITL) bypass vulnerability where a malicious repository could execute UI redressing via Vertical Newline Injection. Previously, if an agent proposed a massive shell command (e.g., heavily padded with newlines to hide a payload like curl | bash), the terminal would truncate the view but leave the "Allow" confirmation options active. This PR prevents users from approving truncated shell commands until they explicitly expand the view.

Details

The vulnerability stems from a disconnect between the visual truncation in MaxSizedBox and the state logic in ToolConfirmationMessage.

This PR makes the following changes:

  • MaxSizedBox.tsx: Added an onOverflowChange prop that triggers whenever the totalHiddenLines calculation changes, exposing the truncation state to the parent component.
  • ToolConfirmationMessage.tsx: Added an isTruncated state. When rendering exec tool confirmations, if isTruncated is true, the standard "Allow" options are replaced with a safety message: ⚠️ Expand to view full command (Press Ctrl+O). The confirmation is locked out until the user expands the view.

How to Validate

  1. Start the Gemini CLI in a test directory.
  2. Ask the agent to run a standard, short command: "Please run echo 'Hello' in my terminal."
    • Expected: The UI displays the command, and standard 1. Allow once / 2. Allow for this session options appear normally.
  3. Ask the agent to run a maliciously padded multi-line command to trigger truncation. You can use this prompt:
    • "Please use the run_shell_command tool to execute this exact string: 'echo Safe' followed by 30 newlines, followed by 'echo Malicious Payload Hidden'"
    • Expected: The UI truncates the display (showing ... hidden (Ctrl+O) ...). The approval options are replaced with ⚠️ Expand to view full command (Press Ctrl+O) and the standard 1. Allow once is not present.
  4. Press Ctrl+O to expand the view.
    • Expected: The UI expands, the hidden "Malicious Payload" becomes visible, and the standard approval options now dynamically appear.

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

likhith-adithya and others added 2 commits March 20, 2026 13:23
…mini#23045)

Co-authored-by: Sri Pasumarthi <sripas@google.com>
Co-authored-by: Sri Pasumarthi <111310667+sripasg@users.noreply.github.com>
…ssages, allowing `MaxSizedBox` to report overflow status.
@likhith-adithya
likhith-adithya requested a review from a team as a code owner March 21, 2026 03:51
@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 delivers a crucial security enhancement by mitigating a Human-in-the-Loop bypass vulnerability. It prevents malicious actors from hiding dangerous commands within truncated UI displays by requiring explicit user expansion before approving any command that has been visually shortened. Additionally, it refines tool exclusion logic for Agent-Controlled Programming modes to ensure smoother integration with IDEs.

Highlights

  • Security Vulnerability Fix: Addressed a critical Human-in-the-Loop (HITL) bypass vulnerability by preventing UI redressing through vertical newline injection in command confirmations.
  • UI Truncation Detection: Implemented a mechanism in MaxSizedBox to detect and expose UI truncation state via a new onOverflowChange prop.
  • Conditional Command Approval: Modified ToolConfirmationMessage to disable command approval for truncated shell commands, requiring users to explicitly expand the view before proceeding.
  • Tool Exclusion in ACP Mode: Extended the exclusion of the ask_user tool to Agent-Controlled Programming (ACP) mode, preventing conversational flow disruptions in IDEs.
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 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 counter productive. 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. ↩

@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 a critical security fix to prevent a Human-in-the-Loop (HITL) bypass vulnerability. The changes ensure that users cannot approve a truncated shell command, forcing them to expand the view to see the full command before confirmation. This is achieved by propagating the truncation state from the MaxSizedBox component to the ToolConfirmationMessage component and conditionally rendering the approval options. The implementation is robust and effectively mitigates the described vulnerability.

I've also included a comment requesting documentation for the change to exclude the ask_user tool in ACP mode, to ensure better traceability and review.

I am having trouble creating individual review comments. Click here to see my feedback.

packages/cli/src/config/config.ts (656-663)

high

This change to exclude ASK_USER_TOOL_NAME in ACP mode is a functional change that is not documented in the pull request description. While the change itself seems reasonable and is tested, all functional changes should be described in the PR summary to ensure proper review and tracking. Please update the PR description to include details about this change, its rationale, and how to validate it.

@likhith-adithya

Copy link
Copy Markdown
Author

/gemini review

MaxSizedBox.tsx: Added an onOverflowChange prop that triggers whenever the totalHiddenLines calculation changes, exposing the truncation state to the parent component.

ToolConfirmationMessage.tsx: Added an isTruncated state. When rendering exec tool confirmations, if isTruncated is true, the standard "Allow" options are replaced with a safety message: ⚠️ Expand to view full command (Press Ctrl+O). The confirmation is locked out until the user expands the view.

@likhith-adithya

Copy link
Copy Markdown
Author

/gemini review

@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 aims to fix a critical HITL bypass vulnerability by preventing users from approving truncated commands, specifically for exec tool confirmations. However, the 'Newline Injection' attack vector still leaves edit and mcp tools vulnerable as they do not currently check the truncation state. Furthermore, a critical race condition has been identified in the new logic that could still allow the bypass in a small time window. It is recommended to extend the safety check to all sensitive tool confirmation types and address the identified race condition to ensure a fail-safe implementation.

Comment on lines 291 to 328
} else if (confirmationDetails.type === 'exec') {
options.push({
label: 'Allow once',
value: ToolConfirmationOutcome.ProceedOnce,
key: 'Allow once',
});
if (isTrustedFolder) {
if (isTruncated) {
options.push({
label: `Allow for this session`,
value: ToolConfirmationOutcome.ProceedAlways,
key: `Allow for this session`,
label: '⚠️ Expand to view full command (Press Ctrl+O)',
value: ToolConfirmationOutcome.Cancel,
key: '⚠️ Expand to view full command (Press Ctrl+O)',
});
if (allowPermanentApproval) {
options.push({
label: 'No, cancel (esc)',
value: ToolConfirmationOutcome.Cancel,
key: 'No, cancel (esc)',
});
} else {
options.push({
label: 'Allow once',
value: ToolConfirmationOutcome.ProceedOnce,
key: 'Allow once',
});
if (isTrustedFolder) {
options.push({
label: `Allow this command for all future sessions`,
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: `Allow for all future sessions`,
label: `Allow for this session`,
value: ToolConfirmationOutcome.ProceedAlways,
key: `Allow for this session`,
});
if (allowPermanentApproval) {
options.push({
label: `Allow this command for all future sessions`,
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: `Allow for all future sessions`,
});
}
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
key: 'No, suggest changes (esc)',
});
}

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 security fix for HITL bypass via UI truncation is incomplete as it only covers exec tool confirmations. Other sensitive tool types, such as edit (which displays file diffs) and mcp (which displays tool arguments), are also susceptible to "Newline Injection" attacks. In these cases, a malicious agent could hide payloads (e.g., malicious code in a diff or dangerous tool arguments) by padding them with newlines, causing the UI to truncate the content.

Currently, the isTruncated state is only updated by the inner MaxSizedBox used for exec tools (line 615), and the getOptions function only checks this state for the exec type (line 292). For edit, info, and mcp tools, the "Allow once" option remains available even if the content is truncated by the outer MaxSizedBox (line 759), allowing a user to inadvertently approve a hidden malicious action.

To remediate this, you should:

  1. Ensure truncation is tracked for all tool types by adding onOverflowChange={setIsTruncated} to the outer MaxSizedBox (or ensuring all tool-specific renderers report their truncation state).
  2. Update the getOptions logic for edit, info, and mcp types to check the isTruncated state and replace the "Allow" options with a warning/expansion instruction, similar to the implementation for exec tools.

@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

A state variable isTruncated was added to the ToolConfirmationMessage component to track whether the command to be executed is truncated due to terminal width limitations. When the command is truncated, the confirmation options are updated to include an option to expand the command for full viewing. The MaxSizedBox component was updated to include an onOverflowChange prop, which is called when the content overflows the maximum height, to set the isTruncated state.

availableTerminalHeight,
terminalWidth,
}) => {
const [isTruncated, setIsTruncated] = useState(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

This is a valuable security improvement for exec confirmations. However, as noted, the same vulnerability exists for other confirmation types (e.g., edit). While extending this security measure to all confirmation types is crucial for comprehensive protection, it falls outside the immediate scope of this pull request. Please consider addressing this broader enhancement in a dedicated follow-up pull request to ensure all potential truncation bypass vulnerabilities are mitigated across the application.

References
  1. A proposed change, such as a security fix, may be considered out of scope for a pull request even if it relates to the code being modified. Such changes should be addressed in a separate pull request.

@gemini-cli gemini-cli Bot added the status/need-issue Pull requests that need to have an associated issue. label Mar 21, 2026
@likhith-adithya

Copy link
Copy Markdown
Author

The original vulnerability allows a complete bypass of the HITL (Human-in-the-Loop) security gate, leading to 1-click Remote Code Execution (RCE) via UI redressing (Newline Injection). While my initial commit patched the exec (shell command) vector, the bot correctly identified that the CLI remains critically vulnerable:

  1. edit Tools: An attacker can hide malicious code injections inside a heavily padded file diff.
  2. mcp Tools: An attacker can hide dangerous remote tool arguments.
    In both cases, the outer MaxSizedBox truncates the display, but the "Allow once" option remains blindly active.

/gemini review

@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 a mechanism to handle truncated command displays in the ToolConfirmationMessage component. It enhances the MaxSizedBox component by adding an onOverflowChange prop, which reports whether its content is truncated. The ToolConfirmationMessage component utilizes this prop to manage an isTruncated state. When a command is truncated, the available confirmation options are dynamically updated to prompt the user to expand the command for full viewing, preventing execution until the full command is visible, thereby improving user safety.

@likhith-adithya

likhith-adithya commented Mar 21, 2026 •

Copy link
Copy Markdown
Author

/gemini review

@likhith-adithya

Copy link
Copy Markdown
Author

/gemini review

@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

The pull request introduces a feature to dynamically adjust confirmation options in ToolConfirmationMessage based on whether the displayed command is truncated, utilizing a new onOverflowChange prop in MaxSizedBox to update an isTruncated state. However, the reviewer identified a high-severity bug: this dynamic change in options creates a circular dependency, potentially leading to an infinite re-render loop (layout oscillation), UI flickering, high CPU usage, or crashes. The suggested fix is to use a stable value for option count in height calculations.

Comment on lines 612 to +615
<MaxSizedBox
maxHeight={bodyContentHeight}
maxWidth={Math.max(terminalWidth, 1)}
onOverflowChange={setIsTruncated}

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 implementation of the isTruncated state introduces a circular dependency that can lead to an infinite re-render loop (layout oscillation). The available height for the command display (availableBodyContentHeight) depends on the number of options shown (getOptions().length), which in turn depends on the isTruncated state. When truncation is detected, the number of options decreases, which increases the available height. If this extra height is enough to fit the command, isTruncated becomes false, restoring the options and triggering truncation again. This can cause the CLI UI to flicker, consume high CPU, or crash. To fix this, the available height calculation should use a stable value for the options count (e.g., the maximum possible for the tool type) instead of the dynamic current count.

@likhith-adithya

Copy link
Copy Markdown
Author

Fixes #23433

@gemini-cli gemini-cli Bot added area/security Issues related to security and removed status/need-issue Pull requests that need to have an associated issue. labels Mar 22, 2026
@gemini-cli

gemini-cli Bot commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Hi there! Thank you for your interest in contributing to Gemini CLI.

To ensure we maintain high code quality and focus on our prioritized roadmap, we have updated our contribution policy (see Discussion #17383).

We only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'. All other community pull requests are subject to closure after 14 days if they do not align with our current focus areas. For this reason, we strongly recommend that contributors only submit pull requests against issues explicitly labeled as 'help-wanted'.

This pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding and for being part of our community!

@gemini-cli gemini-cli Bot closed this Apr 5, 2026
@likhith-adithya

Copy link
Copy Markdown
Author

/gemini

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Hello @likhith-adithya, thank you for your persistence and for bringing this critical security concern to our attention. I understand that this pull request has been closed due to the 14-day inactivity policy, but the security implications you've highlighted regarding HITL bypasses in edit and mcp tools are significant.

Since I am an automated assistant, I cannot reopen this pull request or modify its status. However, if you would like to continue this work, I recommend the following steps:

  1. Open a new Pull Request: Since this PR is closed, the best path forward is to open a fresh PR addressing the remaining vectors (edit and mcp tools) you identified.
  2. Label as 'help-wanted': To ensure your contribution is prioritized and reviewed according to the new policy, please ensure the corresponding issue is labeled as help-wanted in the repository. If you don't have permissions to add labels, please reach out to a maintainer to request the label be added to the issue.

Your analysis of the UI redressing vulnerability is excellent, and extending the isTruncated logic to other tool types is exactly the right approach to ensure the safety of the Human-in-the-Loop gate. Thank you for your commitment to the security of the Gemini CLI.

@sripasg sripasg added the size/m A medium sized PR label Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/security Issues related to security size/m A medium sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vulnerability: HITL Bypass via UI Truncation (Vertical Newline Injection) in Command Confirmations

2 participants