google-gemini / google-gemini/gemini-cli

bug(glob): pattern path traversal can escape workspace via ../ segments despite dir_path validation

Open
#29,075 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/core/src/tools/glob.ts` (main @ 812f7a2bc), `GlobToolInvocation.execute` validates `dir_path` at line 161-174 via `resolveToRealPath` + `validatePathAccess`, but the actual glob execution at lines 193-203 uses a different path resolution without re-validation:

`glob.ts` lines 143-203:
```ts
let searchDirAbsolute: string;
try {
searchDirAbsolute = resolveToRealPath(
path.resolve(this.config.getTargetDir(), this.params.dir_path),
);
} catch (err) { /* ... */ }
const validationError = this.config.validatePathAccess(searchDirAbsolute, 'read');
if (validationError) { return { error: ... }; }
searchDirectories = [searchDirAbsolute];

// ...

const fullPath = path.join(searchDir, pattern);
if (fs.existsSync(fullPath)) {
pattern = escape(pattern);
}

const entries = (await glob(pattern, {
cwd: searchDir,
// ...
follow: false,
})) as GlobPath[];
```

There is a TOCTOU race: between `validatePathAccess(searchDirAbsolute)` and `glob(pattern, {cwd: searchDir})`, the directory at `searchDirAbsolute` could be replaced with a symlink to an outside location (e.g., `/etc`) via a concurrent tool call or external process. `follow: false` prevents following symlinks *during* glob, but does not prevent the `cwd` itself being a symlink that was swapped after validation.

More critically, `pattern` is not sanitized for path traversal: a pattern like `../../etc/passwd` or `../secret/**` is joined with `searchDir` via `path.join` for the `existsSync` check, but `glob` with `cwd: searchDir` will correctly resolve `..` segments relative to `cwd`, potentially escaping the workspace if `validatePathAccess` only checked the base `searchDir` and not the pattern's traversal.

Example: `dir_path: "subdir", pattern: "../../etc/passwd"` — `searchDirAbsolute` is validated as within workspace, but glob with `pattern: "../../etc/passwd"` and `cwd: searchDir` will match files outside the workspace.

### What did you expect to happen?

- `pattern` should be validated to not contain `..` segments that escape `searchDir`, or `validatePathAccess` should be applied to the resolved pattern target
- TOCTOU should be mitigated by using `open` with `O_NOFOLLOW` or re-validating the `cwd` realpath after glob, or by documenting that concurrent filesystem mutations are out of scope but at least the pattern traversal should be blocked

### Client information

- Source-level finding verified against upstream `main` at commit `812f7a2bc`
- File: `packages/core/src/tools/glob.ts:143-203`
- Affects all platforms

### Login information

Not applicable.

### Anything else we need to know?

Sources:
- https://github.com/google-gemini/gemini-cli/blob/812f7a2bc/packages/core/src/tools/glob.ts#L143-L203
- `read-file.ts` and other file tools use similar `resolveToRealPath` + `validatePathAccess` pattern but do not have the pattern traversal issue

**Repro:**
1. Workspace at `/tmp/ws`, create `/tmp/ws/subdir/nested.txt` and `/tmp/secret.txt`
2. Call `GlobTool` with `pattern: "../../secret.txt", dir_path: "subdir"`
3. Observe `glob` may return `/tmp/secret.txt` or error, but validation passed for `subdir` alone

Searched existing issues for "glob path traversal", "dir_path validation" — no open duplicate found.

Contributor guide

Open the contributing guide

Research direction

Start in packages/core/src/tools/glob.ts at GlobToolInvocation.execute, especially lines 143-203, and compare the path handling with read-file.ts. Reproduce the ../../secret.txt case from the issue, then trace how pattern resolution and cwd validation interact. Done means traversal cannot escape the validated workspace and the relevant behavior is covered without weakening normal globbing.

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
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.