Skip to content

fix(security): prevent command injection in findCommand via safe spawnSync#27575

Open
Ashutosh0x wants to merge 3 commits into
google-gemini:mainfrom
Ashutosh0x:fix/command-injection-ide-installer
Open

fix(security): prevent command injection in findCommand via safe spawnSync#27575
Ashutosh0x wants to merge 3 commits into
google-gemini:mainfrom
Ashutosh0x:fix/command-injection-ide-installer

Conversation

@Ashutosh0x
Copy link
Copy Markdown

@Ashutosh0x Ashutosh0x commented May 30, 2026

Summary

Security fix: Replace shell-interpolated execSync calls with safe spawnSync/spawn to prevent command injection via shell metacharacters in two files:

  • packages/core/src/ide/ide-installer.tsfindCommand()
  • packages/core/src/utils/editor.tscommandExists() and commandExistsAsync()

Fixes #27576

Details

Both files construct shell commands via string interpolation with execSync:

// ide-installer.ts (line 32, 41)
child_process.execSync(`where.exe ${command}`)
child_process.execSync(`command -v ${command}`)

// editor.ts (line 108-110, used at line 115 and 124)
function getCommandExistsCmd(cmd: string): string {
  return process.platform === 'win32'
    ? `where.exe ${cmd}`
    : `command -v ${cmd}`;
}
execSync(getCommandExistsCmd(cmd), { stdio: 'ignore' });

If the parameters contain shell metacharacters (;, |, `, $()), these are interpreted by the shell, allowing arbitrary command execution.

Fixes applied:

ide-installer.ts

  1. Replace execSync with spawnSync using argument arrays (shell: false)
  2. Add input validation regex (/^[a-zA-Z0-9.\-_/\\]+$/) as defense-in-depth
  3. Use which instead of command -v (shell builtin incompatible with spawnSync)
  4. Sanitize where.exe output with path.resolve() and null byte check

editor.ts

  1. Replace execSync(getCommandExistsCmd(cmd)) with spawnSync('where.exe'/'which', [cmd])
  2. Replace execAsync(getCommandExistsCmd(cmd)) with spawn('where.exe'/'which', [cmd])
  3. Remove getCommandExistsCmd() helper (no longer needed)
  4. Remove unused exec, execSync, promisify imports

Related Issues

Fixes #27576

How to Validate

  1. Verify existing functionality: Run gemini-cli — editor detection and IDE installation should work as before
  2. Verify injection is blocked: spawnSync/spawn with shell: false prevents shell metacharacter interpretation
  3. Run unit tests: npm test in packages/core

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any) — None, behavior is identical for valid command names
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

…nSync

Replace shell-interpolated execSync calls with spawnSync argument arrays
to prevent command injection in the findCommand() utility function.

Vulnerability:
- execSync(`where.exe `) on Windows (line 32)
- execSync(`command -v `) on Unix (line 41)

Both construct shell commands via string interpolation, which allows
command injection if the 'command' parameter contains shell metacharacters
(e.g., semicolons, pipes, backticks).

Fix:
1. Replace execSync with spawnSync using argument arrays (shell: false)
   to eliminate shell interpretation entirely
2. Add input validation regex to reject command names with metacharacters
   as defense-in-depth
3. Use 'which' instead of 'command -v' on Unix since spawnSync requires
   an executable (command is a shell builtin)
@Ashutosh0x Ashutosh0x requested a review from a team as a code owner May 30, 2026 04:50
@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 potential command injection vulnerability in the findCommand utility. By moving away from shell-interpolated execution and implementing strict input validation, the change ensures that external commands are executed safely without risking arbitrary code execution. This aligns with ongoing security hardening efforts for the project.

Highlights

  • Security Hardening: Replaced vulnerable execSync calls with spawnSync using shell: false to prevent command injection via shell metacharacters.
  • Input Validation: Introduced a regex-based validation check to ensure command names only contain safe, alphanumeric characters and path separators.
  • Platform Compatibility: Updated Unix path resolution to use which instead of command -v to ensure compatibility with spawnSync.
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.

Copy link
Copy Markdown
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 enhances security in packages/core/src/ide/ide-installer.ts by validating the command name against a safe regex pattern and replacing shell-interpolated execSync calls with safe spawnSync executions. The review feedback recommends further securing the output of where.exe by sanitizing and resolving the returned path to prevent potential path traversal or null byte injection vulnerabilities.

Comment on lines +44 to 49
if (result.status === 0 && result.stdout) {
const firstPath = result.stdout.trim().split(/\r?\n/)[0];
if (firstPath) {
return firstPath;
}
}
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 path returned by where.exe is extracted from command output, which is considered an untrusted source. To prevent potential path traversal (..) or null byte injection (\0) vulnerabilities, the path should be sanitized and resolved before being returned and executed, in accordance with our security rules.

Suggested change
if (result.status === 0 && result.stdout) {
const firstPath = result.stdout.trim().split(/\r?\n/)[0];
if (firstPath) {
return firstPath;
}
}
if (result.status === 0 && result.stdout) {
const firstPath = result.stdout.trim().split(/\r?\n/)[0];
if (firstPath && !firstPath.includes('\0') && !firstPath.includes('..')) {
return path.resolve(firstPath);
}
}
References
  1. Sanitize file paths extracted from untrusted sources, such as command output, to prevent path traversal (..), null byte injection (\0), and other vulnerabilities.

@gemini-cli gemini-cli Bot added the status/need-issue Pull requests that need to have an associated issue. label May 30, 2026
Address Gemini Code Assist review: add path.resolve() and null byte
check to sanitize the path returned by where.exe, preventing potential
path traversal or null byte injection from untrusted command output.
…commandExistsAsync

Same vulnerability as ide-installer.ts: execSync with shell-interpolated
command names via getCommandExistsCmd(). Replaced with spawnSync/spawn
using argument arrays (shell: false).

- Removed getCommandExistsCmd() helper (no longer needed)
- Removed unused exec, execSync, promisify imports
- commandExists() now uses spawnSync('where.exe'/'which', [cmd])
- commandExistsAsync() now uses spawn('where.exe'/'which', [cmd])
@gemini-cli gemini-cli Bot added area/security Issues related to security priority/p2 Important but can be addressed in a future release. and removed status/need-issue Pull requests that need to have an associated issue. labels May 30, 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 priority/p2 Important but can be addressed in a future release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security: Command injection in findCommand via shell-interpolated execSync

1 participant