Skip to content

fix(core): preserve trailing newlines in shell execution - #23705

Closed
Aaxhirrr wants to merge 3 commits into
google-gemini:mainfrom
Aaxhirrr:fix/shell-trailing-newline-20755
Closed

fix(core): preserve trailing newlines in shell execution#23705
Aaxhirrr wants to merge 3 commits into
google-gemini:mainfrom
Aaxhirrr:fix/shell-trailing-newline-20755

Conversation

@Aaxhirrr

Copy link
Copy Markdown
Contributor

Summary

Fixes a regression in run_shell_command where trailing newlines were stripped before execution, which breaks heredocs and other multiline shell constructs that require a final newline delimiter.

Details

This change keeps the fix narrowly scoped to the execution path.

  • Preserve trailing whitespace only when preparing the command for execution.
  • Keep existing trimmed behavior for confirmation text, command-root parsing, and allowlist/policy-related parsing so the patch has a smaller blast radius.
  • Update the Unix pgrep wrapper so newline-terminated commands close cleanly instead of being trimmed and forced onto a same-line ;.
  • Add focused regression coverage for:
    • wrapped heredoc commands
    • wrapped comment-only commands
    • Linux shell execution wrapping
    • a bash-only integration case driven by fake responses

No documentation changes were needed.

Related Issues

Fixes #20755

How to Validate

  1. Run the core shell regressions:

    npm run test --workspace @google/gemini-cli-core -- shell.test.ts shell-utils.test.ts

    Expected result:

    • shell.test.ts passes
    • shell-utils.test.ts passes
  2. In a bash environment, run the targeted integration regression:

    npm run test:integration:sandbox:none -- run_shell_command.test.ts -t "should preserve trailing newlines for heredoc shell commands"

    Expected result:

    • the test passes
    • the recorded run_shell_command args still end with \n
    • the output contains TRAILING_NEWLINE_20755
    • the output does not contain here-document delimited by end-of-file or syntax error: unexpected end of file
  3. Manual sanity check in a bash environment:

    Execute a shell tool call with:

    cat <<EOF
    test
    EOF

    Expected result:

    • command succeeds
    • output contains test
    • no heredoc EOF or syntax error is emitted
  4. Optional extra edge case:

    Execute a comment-only command ending in a newline:

    # comment

    Expected result:

    • no unexpected EOF caused by trimming

Note:

  • The new integration regression is bash-only, so it is skipped on Windows.
  • I validated the unit test coverage locally on Windows with npm run.

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

@Aaxhirrr
Aaxhirrr requested review from a team as code owners March 24, 2026 20:17
@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 addresses a regression in the run_shell_command functionality that caused trailing newlines to be inadvertently stripped from shell commands prior to execution. This stripping broke multiline shell constructs such as heredocs. The implemented solution ensures that trailing newlines are preserved when commands are prepared for execution, while carefully maintaining the existing trimming behavior for other non-execution-critical contexts. This targeted fix restores proper functionality for complex shell commands and enhances the robustness of shell execution.

Highlights

  • Preserved Trailing Newlines: Trailing newlines are now preserved in shell commands specifically during execution, fixing a regression that affected multiline constructs like heredocs.
  • Scoped Fix: The change is narrowly scoped to the execution path, maintaining existing trimmed behavior for other contexts such as confirmation text and command parsing.
  • Updated pgrep Wrapper: The Unix pgrep wrapper logic was updated to correctly handle newline-terminated commands, preventing unintended trimming.
  • Enhanced Test Coverage: New regression tests were added for wrapped heredoc commands, comment-only commands, Linux shell execution wrapping, and a bash-only integration case to ensure the fix is robust.
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 changes to preserve trailing newlines in shell commands, particularly for heredocs and comment-only commands. The stripShellWrapper utility now accepts an option to retain trailing whitespace, and the ShellToolInvocation logic has been updated to correctly wrap commands that end with newlines. New unit and integration tests have been added to validate this behavior. A security concern was identified where the tempFilePath variable is directly interpolated into a shell command, posing a potential command injection vulnerability if the path contains special characters.

Comment thread packages/core/src/tools/shell.ts Outdated
@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/core Issues related to User Interface, OS Support, Core Functionality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! labels Mar 24, 2026
@Aaxhirrr

Copy link
Copy Markdown
Contributor 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 introduces changes to correctly preserve trailing newlines in shell commands, particularly for heredocs and comment-only commands. It modifies the stripShellWrapper utility to include an option for preserving trailing whitespace and updates the ShellToolInvocation to use this option and adjust command wrapping logic accordingly. Additionally, it incorporates escapeShellArg for temporary file paths in wrapped commands for improved robustness. New unit and integration tests have been added to validate these changes. I have no feedback to provide on the review comments as there were none.

@cocosheng-g
cocosheng-g self-requested a review March 27, 2026 14:23
@cocosheng-g

Copy link
Copy Markdown
Contributor

Please resolve conflicts @Aaxhirrr

@Aaxhirrr
Aaxhirrr force-pushed the fix/shell-trailing-newline-20755 branch from f736516 to 8fb948f Compare March 27, 2026 18:57
@Aaxhirrr

Copy link
Copy Markdown
Contributor 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 ensures that trailing whitespace and newlines are preserved when processing shell commands, which is particularly important for heredocs and background tasks. Key changes include refactoring stripShellWrapper to support a preserveTrailingWhitespace option and updating the command wrapping logic in ShellTool to handle line terminators correctly and escape temporary file paths. Comprehensive integration and unit tests have been added to verify these improvements. I have no feedback to provide as there were no review comments to evaluate.

@Aaxhirrr

Copy link
Copy Markdown
Contributor Author

Please resolve conflicts @Aaxhirrr

Thanks @cocosheng-g , I’ve rebased the branch, resolved the conflict in packages/core/src/tools/shell.ts, and pushed the updated branch.

I also added a small regression test to cover the background-command whitespace case that came up while rechecking the fix.

Could you please take a look when you get a chance?

Comment thread packages/core/src/tools/shell.ts Outdated
if (!command.endsWith('&')) command += ';';
return `{ ${command} }; __code=$?; pgrep -g 0 >${tempFilePath} 2>&1; exit $__code;`;
let command = strippedCommand;
const hasTrailingLineTerminator = /[\r\n]$/.test(command);

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.

Here, this matches either \r or \n as the last character. Since this code path
only runs on non-Windows (line 238), the only line terminator bash actually
understands is \n. A bare \r is just a regular character to bash - it
doesn't separate tokens.

So if a command were to end with a bare \r (no \n), the wrapping would
produce:

 { echo hello\r}; __code=$?; ...

Bash would read \r} as one word instead of seeing } as the group-closing
keyword, leaving the { unmatched and causing a syntax error.

A small tweak would prevent this:

const hasTrailingLineTerminator = /\r?\n$/.test(command);

This still handles \n (Unix) and \r\n (Windows-style line endings pasted
into a Unix session), but a bare \r would correctly fall through to the
regular command += ';' path where it's harmless.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, this was a good catch.

I updated the wrapper logic so it only treats \n or \r\n as a real trailing line terminator, and a bare \r now goes through the normal path instead of being treated like a newline. I also added a regression test for that case while keeping the heredoc and comment-only trailing-newline coverage.

I rebased the branch and pushed the updated fix. Could you please take another look when you get a chance?

@Aaxhirrr
Aaxhirrr force-pushed the fix/shell-trailing-newline-20755 branch from 8fb948f to beefa73 Compare April 6, 2026 21:22
@scidomino

Copy link
Copy Markdown
Collaborator

Closing as this seems to have been fixed in #25537

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

Labels

area/core Issues related to User Interface, OS Support, Core Functionality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p2 Important but can be addressed in a future release. size/l A large sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shell Tool Trailing Newline Trimming

5 participants