Skip to content

fix(cli): avoid shell interpolation in sandbox build and network setup - #29492

Open
princeraj2572 wants to merge 3 commits into
google-gemini:mainfrom
princeraj2572:fix/sandbox-shell-injection
Open

princeraj2572 wants to merge 3 commits into
google-gemini:mainfrom
princeraj2572:fix/sandbox-shell-injection

Conversation

@princeraj2572

@princeraj2572 princeraj2572 commented Sep 24, 2026 •

Copy link
Copy Markdown

Summary

Under BUILD_SANDBOX=1, start_sandbox built the sandbox image with execSync and a shell string that interpolated the checkout path (gcRoot) and the project sandbox.Dockerfile path. Shell metacharacters in either path (for example a checkout in /tmp/evil; touch /tmp/pwned; echo) were executed on the host, outside the sandbox. This PR removes shell parsing from those calls.

Details

  • Image build: execFileSync('node', ['scripts/build_sandbox.js', '-s', ...buildArgs], { cwd: gcRoot, ... }) replaces cd ${gcRoot} && node .... Paths are argv entries.
  • Container networks: the network inspect || network create shell chaining becomes an ensureNetwork() helper using execFileSync with try/catch. Behavior is unchanged: any failed inspect falls through to create.
  • network connect uses execFileAsync instead of a shell string. Its inputs were already whitelisted or constant, so this is for consistency.
  • Left unchanged on purpose: GEMINI_SANDBOX_PROXY_COMMAND (a user-supplied command, run with shell: true by design) and the fixed until timeout 0.25 curl ... wait.

Related Issues

Fixes #29070

How to Validate

  • npx vitest run packages/cli/src/utils/sandbox.test.ts (44 tests pass). The networkAccess: false test now expects execFileSync('docker', ['network', 'create', '--internal', 'gemini-cli-sandbox']).
  • Injection check with a POSIX shell and a checkout path named evil; touch PWNED; echo: the old execSync pattern creates PWNED; the execFileSync pattern does not.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any): none
  • Validated on required platforms/methods:
    • Windows
      • npm run (unit tests only; Docker/Podman/Seatbelt and macOS/Linux not run locally)

Under BUILD_SANDBOX=1 the CLI built the sandbox image with execSync and a
shell string that interpolated the checkout path and the project
sandbox.Dockerfile path, so shell metacharacters in either path were
executed on the host. The container network inspect/create calls used the
same pattern.

Use execFileSync with argv arrays (and cwd instead of `cd`) so no shell
parses these values, and replace the `inspect || create` shell chaining with
a try/catch.

Fixes google-gemini#29070
@princeraj2572
princeraj2572 requested a review from a team as a code owner September 24, 2026 19:24
@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 enhances the security of the sandbox environment setup by eliminating shell interpolation in command execution. By transitioning from shell-string commands to direct process execution with argument arrays, the system is now protected against potential command injection via path names containing shell metacharacters. The changes cover sandbox image building, network inspection and creation, and proxy network connection, while maintaining existing functionality.

Highlights

  • Security Improvement: Replaced shell-based command execution with direct process spawning using execFileSync and execFileAsync to prevent shell injection vulnerabilities.
  • Sandbox Build Logic: Updated the sandbox image build process to pass arguments as an array rather than a shell-interpolated string, ensuring safe handling of file paths.
  • Network Management: Introduced an ensureNetwork helper function to manage Docker/Podman network creation without relying on shell chaining (||), improving robustness and security.
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 github-actions Bot added the size/m A medium sized PR label Sep 24, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 79
  • Additions: +52
  • Deletions: -27
  • Files changed: 2

@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 refactors sandbox-related executions to use execFileSync and execFileAsync instead of shell-based execSync and execAsync, preventing potential shell injection and metacharacter interpretation issues. Feedback on these changes points out a cross-platform compatibility bug on Windows where splitting gcPath with a forward slash (/packages/) will fail and cause an ENOTDIR crash. It is recommended to normalize path separators before splitting and to use process.execPath instead of the global 'node' command.

Comment on lines +451 to +460
// Pass paths as argv (no shell) so metacharacters in the checkout or
// project path cannot be interpreted as shell syntax.
execFileSync('node', ['scripts/build_sandbox.js', '-s', ...buildArgs], {
cwd: gcRoot,
stdio: 'inherit',
env: {
...process.env,
GEMINI_SANDBOX: command, // in case sandbox is enabled via flags (see config.ts under cli package)
},
);
});

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

Issues Identified

  1. Windows Path Splitting Bug (ENOTDIR crash):
    The gcRoot variable is computed on line 435 using gcPath.split('/packages/')[0]. On Windows, paths use backslashes (\\), so this split fails and leaves gcRoot as the full file path of the executable (e.g., C:\\Users\\...\\packages\\cli\\bin\\gemini.js). Passing a file path to cwd in execFileSync will throw an ENOTDIR error and crash the process.

  2. Node Executable Robustness:
    Using 'node' directly relies on the global PATH, which might not point to the correct Node.js version or might not be available in some environments. Using process.execPath ensures the exact same Node.js binary running the CLI is used to execute the script.

Recommendation

Normalize the path separators to forward slashes before splitting to ensure cross-platform compatibility, and use process.execPath instead of 'node'.

        // Pass paths as argv (no shell) so metacharacters in the checkout or
        // project path cannot be interpreted as shell syntax.
        const crossPlatformGcRoot = gcRoot.replace(/\\/g, '/').split('/packages/')[0];
        execFileSync(process.execPath, ['scripts/build_sandbox.js', '-s', ...buildArgs], {
          cwd: crossPlatformGcRoot,
          stdio: 'inherit',
          env: {
            ...process.env,
            GEMINI_SANDBOX: command, // in case sandbox is enabled via flags (see config.ts under cli package)
          },
        });

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Re: the Windows gcRoot split. This isn't reachable on Windows: the guard just above, gcPath.includes('gemini-cli/packages/'), uses forward slashes, so a backslash path throws "Cannot build sandbox using installed gemini binary" before gcRoot is computed. The gcPath.split('/packages/')[0] logic is also unchanged from the previous cd ${gcRoot} version, so this PR doesn't introduce or worsen it. Making the build path Windows-aware would mean reworking that guard as well, which is outside the scope of #29070 (shell injection).

@gemini-cli

gemini-cli Bot commented Oct 2, 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 only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.

This PR will be closed in 7 days if it remains without that designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

This branch has not been deployed

No deployments
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 status/pr-nudge-sent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security(sandbox): host shell injection via BUILD_SANDBOX path interpolation in sandbox.ts

1 participant