gemini-cli - 💡(How to fix) Fix Path traversal in Chromium agents/extensions/install.py allows arbitrary directory deletion via shutil.rmtree [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
google-gemini/gemini-cli#25250Fetched 2026-04-14 05:56:32
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Author
Timeline (top)
labeled ×2commented ×1

The process_extensions() function in agents/extensions/install.py (part of the Chromium source tree at chromium/src/agents/extensions/install.py) passes user-supplied extension names from CLI arguments directly to shutil.rmtree() without any path sanitization or validation.

When the remove command is invoked and the extension name contains an underscore character (_), the code bypasses the Gemini CLI's own uninstall command and calls shutil.rmtree() directly on a path constructed by joining the user-supplied name to ~/.gemini/extensions/:

# install.py, lines 390-394
elif command == 'remove':
    if '_' in extension:
        # gemini rejects extension names with _ in them so if they're
        # already installed we need to delete them directly
        shutil.rmtree(get_global_extension_dir() / extension)

Because pathlib.Path concatenation does not resolve or reject .. path components, an attacker can supply a crafted extension name containing path traversal sequences to recursively delete arbitrary directories.

Error Message

raise Error(f"Invalid extension name: '{extension}'")

Root Cause

  1. argparse accepts any string as an extension name with zero validation (line 463-466)
  2. The if '_' in extension condition at line 391 bypasses the Gemini CLI (which would perform its own validation) and routes directly to shutil.rmtree()
  3. pathlib.Path / operator preserves .. components — no resolution or containment check
  4. No validation anywhere that the resolved path stays within ~/.gemini/extensions/

Code Example

# install.py, lines 390-394
elif command == 'remove':
    if '_' in extension:
        # gemini rejects extension names with _ in them so if they're
        # already installed we need to delete them directly
        shutil.rmtree(get_global_extension_dir() / extension)

---

vpython3 agents/extensions/install.py remove 'a_b/../../../.ssh'

---

def _validate_extension_name(extension: str) -> None:
    if '/' in extension or '\\' in extension or '..' in extension:
        raise Error(f"Invalid extension name: '{extension}'")
RAW_BUFFERClick to expand / collapse

Summary

The process_extensions() function in agents/extensions/install.py (part of the Chromium source tree at chromium/src/agents/extensions/install.py) passes user-supplied extension names from CLI arguments directly to shutil.rmtree() without any path sanitization or validation.

When the remove command is invoked and the extension name contains an underscore character (_), the code bypasses the Gemini CLI's own uninstall command and calls shutil.rmtree() directly on a path constructed by joining the user-supplied name to ~/.gemini/extensions/:

# install.py, lines 390-394
elif command == 'remove':
    if '_' in extension:
        # gemini rejects extension names with _ in them so if they're
        # already installed we need to delete them directly
        shutil.rmtree(get_global_extension_dir() / extension)

Because pathlib.Path concatenation does not resolve or reject .. path components, an attacker can supply a crafted extension name containing path traversal sequences to recursively delete arbitrary directories.

Reproduction

vpython3 agents/extensions/install.py remove 'a_b/../../../.ssh'

This resolves ~/.gemini/extensions/a_b/../../../.ssh to ~/.ssh and recursively deletes it.

Verified Payloads

PayloadResolves ToImpact
a_b/../../../.ssh~/.sshDestroys SSH keys
has_underscore/../../../.config~/.configDestroys app configs
mal_ext/../../../Documents~/DocumentsDestroys user documents

Root Cause

  1. argparse accepts any string as an extension name with zero validation (line 463-466)
  2. The if '_' in extension condition at line 391 bypasses the Gemini CLI (which would perform its own validation) and routes directly to shutil.rmtree()
  3. pathlib.Path / operator preserves .. components — no resolution or containment check
  4. No validation anywhere that the resolved path stays within ~/.gemini/extensions/

Secondary Issue

The find_extensions_dir_for_extension() function at line 222-228 has the same unsanitized path concatenation in its existence check, affecting the add command flow. A traversal payload can cause arbitrary directories to be linked as Gemini extensions.

Suggested Fix

def _validate_extension_name(extension: str) -> None:
    if '/' in extension or '\\' in extension or '..' in extension:
        raise Error(f"Invalid extension name: '{extension}'")

Call this at the top of the for extension in extensions: loop in process_extensions() (line 373).

Affected Code

  • Repository: https://github.com/chromium/chromium
  • File: agents/extensions/install.py
  • Lines: 390-394 (primary), 222-228 (secondary)
  • Last commit on file: 75a5791ce98f61595e7428318e960e0a11a71b94 (2026-04-02)

This file is a wrapper around the gemini extensions commands. The vulnerability exists because the wrapper bypasses the Gemini CLI's own input validation when extension names contain underscores.

extent analysis

TL;DR

The most likely fix is to add input validation to the process_extensions() function to prevent path traversal attacks by checking for invalid characters in the extension name.

Guidance

  • Validate the extension name at the beginning of the process_extensions() function to prevent path traversal attacks.
  • Use the suggested _validate_extension_name() function to check for invalid characters such as /, \, and .. in the extension name.
  • Consider adding additional validation to ensure the resolved path stays within the ~/.gemini/extensions/ directory.
  • Review the find_extensions_dir_for_extension() function to ensure it also performs proper path validation to prevent similar vulnerabilities.

Example

def _validate_extension_name(extension: str) -> None:
    if '/' in extension or '\\' in extension or '..' in extension:
        raise Error(f"Invalid extension name: '{extension}'")

# Call the validation function at the top of the process_extensions() loop
for extension in extensions:
    _validate_extension_name(extension)
    # ... rest of the function ...

Notes

The provided fix only addresses the primary issue and does not account for potential secondary issues or edge cases. Additional review and testing are necessary to ensure the fix is comprehensive and effective.

Recommendation

Apply the suggested workaround by adding the _validate_extension_name() function and calling it at the beginning of the process_extensions() loop to prevent path traversal attacks. This fix is a straightforward and effective way to address the vulnerability without requiring significant changes to the existing codebase.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING