google-gemini / google-gemini/gemini-cli

security(extensions): extension name path traversal allows write outside extensions directory

Open
#29,076 0 comments 0 reactions 0 assignees View on GitHub
area/security status/need-triage
Dominant language
TypeScript
Stars
107k
Forks
14.6k
Avg merge
2d 3h
Merged PRs (30d)
45

Description

### What happened?

In `packages/cli/src/config/extension-manager.ts` and `packages/cli/src/config/extensions/storage.ts` (main @ 812f7a2bc), extension installation uses the extension's `name` field directly as a filesystem path component without sufficient sanitization:

`storage.ts` and `extension-manager.ts`:
```ts
const extensionPath = path.join(extensionsDir, extensionName);
await fs.promises.cp(source, extensionPath, { recursive: true });
```

While `validateName` checks `/^[a-zA-Z0-9-]+$/`, this validation is only applied during `inferInstallMetadata` for GitHub sources, not for local filesystem sources or when `extension.json` is manually crafted. An extension with `name: "../../.ssh"` or `name: "evil/subdir"` in its `extension.json` could cause the install to write outside the intended `~/.gemini/extensions` directory.

Additionally, `copyExtension` uses `fs.promises.cp` with `recursive: true` without verifying that `source` is within expected bounds, and `makeWritableRecursive` follows symlinks without limit, enabling symlink attacks to overwrite arbitrary files.

A malicious extension (e.g., from a compromised git repository) could declare:
```json
{
"name": "../.config/autostart",
"version": "1.0.0"
}
```
and upon `gemini extensions install` or auto-update, write files to `~/.config/autostart` or similar sensitive locations.

### What did you expect to happen?

- `extensionName` should be strictly validated against path traversal (`..`, `/`, `\`) at all entry points, not just GitHub inference, with explicit rejection if `path.join(extensionsDir, name)` resolves outside `extensionsDir` (via `isWithinRoot` check)
- `copyExtension` should verify `source` is within expected temp directory and `destination` is within extensions directory before copying
- `makeWritableRecursive` should limit symlink depth and not follow symlinks that escape the destination

### Client information

- Source-level finding verified against upstream `main` at commit `812f7a2bc`
- Files: `packages/cli/src/config/extension-manager.ts:1266-1272`, `packages/cli/src/config/extensions/storage.ts`, `packages/cli/src/config/extension.ts`
- Affects all platforms

### Login information

Not applicable.

### Anything else we need to know?

Sources:
- https://github.com/google-gemini/gemini-cli/blob/812f7a2bc/packages/cli/src/config/extension-manager.ts#L1266-L1272
- https://github.com/google-gemini/gemini-cli/blob/812f7a2bc/packages/cli/src/config/extensions/storage.ts
- `validateName` regex exists but not consistently enforced

**Repro:**
1. Create a local extension dir with `extension.json` containing `{"name": "../evil", "version": "1.0.0"}`
2. `gemini extensions install /path/to/evil-extension`
3. Observe files written to parent directory of extensions dir

Searched existing issues for "extension path traversal", "extension name validation" — no open duplicate found.

Contributor guide

Open the contributing guide

Research direction

Start in packages/cli/src/config/extension-manager.ts around lines 1266-1272, then trace extension metadata handling in packages/cli/src/config/extension.ts and copying in packages/cli/src/config/extensions/storage.ts. Reproduce the local extension case, then verify invalid names, source and destination boundaries, and symlink handling prevent writes outside the extensions directory.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.