Skip to content

Comments

security: strip deceptive Unicode characters from terminal output#19026

Merged
ehedlund merged 7 commits intogoogle-gemini:mainfrom
ehedlund:unicode
Feb 20, 2026
Merged

security: strip deceptive Unicode characters from terminal output#19026
ehedlund merged 7 commits intogoogle-gemini:mainfrom
ehedlund:unicode

Conversation

@ehedlund
Copy link
Contributor

@ehedlund ehedlund commented Feb 13, 2026

Summary

This PR addresses security concerns regarding deceptive Unicode characters (e.g., BiDi overrides, zero-width characters) being used to obfuscate terminal output and tool call parameters.

Details

  • Core Sanitization: Updated stripUnsafeCharacters in packages/cli/src/ui/utils/textUtils.ts to strip:
    • BiDi Control Characters: (U+200E, U+200F, U+202A-U+202E, U+2066-U+2069).
    • Zero-Width Characters: (U+200B, U+200C, U+FEFF).
  • Global Markdown Protection: Integrated stripUnsafeCharacters into RenderInline in packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx.
  • Targeted Protection: Applied explicit sanitization to critical fields in ToolConfirmationMessage.tsx (MCP tool/server names, diff filenames, and file diff content).

Related Issues

https://github.com/google-gemini/maintainers-gemini-cli/issues/1206

How to Validate

  1. Run unit tests: npm test -w @google/gemini-cli -- src/ui/utils/textUtils.test.ts
  2. Manual verification:
    • Trigger a tool confirmation with BiDi characters.
    • Verify they are stripped in the displayed output.

Pre-Merge Checklist

  • Added/updated tests
  • Validated on MacOS (npm run)

@ehedlund ehedlund requested a review from a team as a code owner February 13, 2026 19:44
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ehedlund, 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 significantly enhances the security posture of the CLI by implementing robust sanitization of terminal output. It prevents potential obfuscation attacks that leverage deceptive Unicode characters, such as BiDirectional overrides and zero-width characters, to mislead users about tool parameters or file names. By systematically stripping these unsafe characters, the changes ensure that information displayed to the user is accurate and untampered, thereby mitigating a class of security vulnerabilities.

Highlights

  • Enhanced Unicode Sanitization: The stripUnsafeCharacters function has been updated to remove deceptive Unicode characters, including BiDi control characters (U+202A-U+202E, U+2066-U+2069) and zero-width characters (U+200B, U+200C, U+FEFF), while preserving ZWJ (U+200D) for complex emojis.
  • Global Markdown Component Protection: The enhanced stripUnsafeCharacters function is now integrated into InlineMarkdownRenderer.tsx, providing automatic sanitization for text rendered in markdown-based UI components like AskUserDialog, InfoMessage, and WarningMessage.
  • Explicit MCP Tool/Server Name Sanitization: Explicit sanitization using sanitizeForDisplay has been applied to MCP tool names, server names, and filenames within ToolConfirmationMessage.tsx to directly protect against Unicode-based obfuscation in confirmation dialogs.
  • Comprehensive Unit Testing: New unit tests have been added to textUtils.test.ts to thoroughly validate the stripping of BiDi override characters, all BiDi control characters, zero-width characters, and to confirm the correct preservation of ZWJ.
Changelog
  • packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
    • Applied sanitizeForDisplay to mcpProps.toolName, mcpProps.serverName, and confirmationDetails.fileName to prevent deceptive Unicode characters in displayed names.
  • packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx
    • Imported stripUnsafeCharacters and applied it to the text prop to sanitize all markdown-rendered content.
  • packages/cli/src/ui/utils/textUtils.test.ts
    • Added new test cases to verify the correct stripping of BiDi override characters, all BiDi control characters, and zero-width characters.
    • Included a test to confirm that ZWJ (Zero Width Joiner) characters are preserved to maintain complex emoji integrity.
  • packages/cli/src/ui/utils/textUtils.ts
    • Updated the stripUnsafeCharacters function's regular expression to include BiDi control characters (U+202A-U+202E, U+2066-U+2069) and zero-width characters (U+200B, U+200C, U+FEFF).
    • Modified documentation to reflect the newly stripped character types and explicitly mention the preservation of ZWJ (U+200D).
    • Resolved an ESLint warning related to targeting control characters in a regular expression.
Activity
  • No specific activity (comments, reviews, or progress updates) has been recorded for this pull request yet.
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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Feb 13, 2026
Copy link
Contributor

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

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 valuable security enhancement by stripping deceptive Unicode characters from terminal output, utilizing the stripUnsafeCharacters utility in InlineMarkdownRenderer and ToolConfirmationMessage. However, a significant vulnerability remains as the sanitization logic was overlooked for the file diff display within the ToolConfirmationMessage, potentially exposing users to deceptive file modifications. This oversight is addressed by recommending the application of stripUnsafeCharacters to confirmationDetails.fileDiff, aligning with the principle of defense-in-depth. Further improvements are suggested to make the BiDi character stripping more comprehensive by including Left-to-Right Mark (LRM) and Right-to-Left Mark (RLM), with corresponding updates recommended for the stripping logic, documentation, and tests.

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

packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx (334)

security-high high

The fileDiff content passed to DiffRenderer is not sanitized for deceptive Unicode characters (such as BiDi overrides or zero-width characters). While this PR correctly adds sanitization for the fileName on line 334, the actual diff content on line 333 remains vulnerable. An attacker (e.g., via a malicious LLM response) could use BiDi characters to obfuscate the changes being shown to the user, potentially tricking them into approving a harmful file modification.

Since this PR's primary goal is to strip deceptive Unicode characters from terminal output, this rendering path should also be protected. I recommend applying stripUnsafeCharacters to confirmationDetails.fileDiff before passing it to DiffRenderer.

References
  1. Always treat user-provided data as untrusted and apply proper validation and sanitization at the point of use, even if it is believed to have been filtered or sanitized upstream. This follows the principle of defense-in-depth.

packages/cli/src/ui/utils/textUtils.ts (109)

high

For more comprehensive protection against deceptive BiDi characters, please also strip Left-to-Right Mark (LRM, U+200E) and Right-to-Left Mark (RLM, U+200F). This JSDoc should be updated to reflect their inclusion.

 * - BiDi control chars (U+200E, U+200F, U+202A-U+202E, U+2066-U+2069)

packages/cli/src/ui/utils/textUtils.ts (130)

high

To implement the stripping of LRM and RLM characters, please add \u200E and \u200F to this regular expression. These are invisible characters that can alter the visual order of text and should be removed as part of this security enhancement.

    /["\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F\u200E\u200F\u202A-\u202E\u2066-\u2069\u200B\u200C\uFEFF]/g,

packages/cli/src/ui/utils/textUtils.test.ts (342-346)

high

Please update this test case to also verify the stripping of LRM (\u200E) and RLM (\u200F) characters, ensuring test coverage for the more comprehensive BiDi character stripping.

      it('should strip all BiDi control characters (LRM, RLM, U+202A-U+202B\u202C\u202D\u202E, U+2066-U+2069)', () => {
        const bidiChars =
          '\u200E\u200F\u202A\u202B\u202C\u202D\u202E\u2066\u2067\u2068\u2069';
        expect(stripUnsafeCharacters('a' + bidiChars + 'b')).toBe('ab');
      });

@ehedlund
Copy link
Contributor Author

/gemini review

Copy link
Contributor

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

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 effectively hardens the CLI against deceptive Unicode characters by introducing and applying stripUnsafeCharacters in key areas, including the general markdown renderer and diff displays. This is a great security improvement.

However, I've identified a critical issue where sanitizeForDisplay is used for displaying filenames and tool/server names. This function collapses whitespace, which can misrepresent the string being displayed. This is especially dangerous when displaying shell commands for confirmation, as it can alter the command's meaning. My review includes suggestions to use stripUnsafeCharacters instead to ensure accurate representation while still providing the intended security benefits.

ehedlund and others added 3 commits February 18, 2026 15:04
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@spencer426 spencer426 self-requested a review February 18, 2026 23:47
@galz10 galz10 self-requested a review February 20, 2026 19:04
@ehedlund ehedlund added this pull request to the merge queue Feb 20, 2026
Merged via the queue into google-gemini:main with commit aed348a Feb 20, 2026
27 checks passed
@ehedlund ehedlund deleted the unicode branch February 20, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants