google-gemini / google-gemini/gemini-cli
security(core): project .env can inject execution-affecting GIT_* variables (GIT_EXEC_PATH, GIT_SSH_COMMAND, GIT_PROXY_COMMAND) into internal git operations; sanitization only strips GIT_CONFIG_*
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### What happened?
Gemini CLI loads a project's `.env` file into `process.env` at startup (trusted workspaces load **all** keys), and its internal git operations are executed with an environment derived from `process.env`. The git-environment hardening that does exist (`getSafeGitEnv()` in `packages/core/src/utils/gitUtils.ts`, `sanitizeEnvironment()` usage in `packages/core/src/services/gitService.ts`, and the equivalent logic in `shellExecutionService.prepareExecution`) only neutralizes `GIT_CONFIG_*` / `GIT_CONFIG_PARAMETERS`. It does not touch the other `GIT_*` variables that change *which binaries and helpers git executes*.
As a result, a malicious-but-trusted repository can ship a `.env` containing, for example:
```env
GIT_EXEC_PATH=C:\Users\victim\AppData\Local\Temp\evil
GIT_SSH_COMMAND=calc.exe
GIT_PROXY_COMMAND=cmd /c calc.exe
```
and, merely by starting Gemini CLI inside that repository (folder-trusted, e.g. once via the trust prompt or when folder trust is disabled), trigger attacker-controlled code through completely ordinary, non-model, pre-approval git calls such as:
- checkpointing initialization — `GitService.initialize()` → `spawnAsync('git', ['--version'], { env: getSafeGitEnv() })` and subsequent shadow-repo commits (git resolves subcommand binaries from `GIT_EXEC_PATH`; `core.hooksPath` is already neutralized, but these env vectors are not)
- extension install/update — `cloneFromGit()` uses `simple-git` with `getSafeGitEnv()`, where `git.fetch`/`clone` will invoke `GIT_SSH_COMMAND`/`GIT_PROXY_COMMAND` for non-https remotes
- memory/git integration paths using `getAbsoluteGitDir()`
The codebase demonstrates awareness of this exact risk class: `DEFAULT_EXCLUDED_ENV_VARS` blocks `GEMINI_CLI_IDE_SERVER_STDIO_COMMAND`/`_ARGS` from project env files precisely because they name executables — but no equivalent protection exists for git's executable/helper env vars.
### What did you expect to happen?
Environment sanitization for internal git invocations should also strip or pin execution-affecting variables, e.g.:
- In `getSafeGitEnv()` (and the parallel logic in `gitService.getShadowRepoEnv()` / `shellExecutionService.prepareExecution`): delete `GIT_EXEC_PATH`, `GIT_PROXY_COMMAND`, `GIT_SSH_COMMAND`, `GIT_SSH_VARIANT`, `GIT_ALTERNATE_OBJECT_DIRECTORIES`, `GIT_TEMPLATE_DIR`, `GIT_REPLACE_REF_BASE`, `GIT_CEILING_DIRECTORIES` (as applicable), rather than only `GIT_CONFIG_*`.
- Alternatively/additionally, extend `DEFAULT_EXCLUDED_ENV_VARS` so project `.env` files cannot introduce these variables at all.
This restores the same isolation intent that already exists for git config (credential.helper, core.hooksPath, etc.) but which currently stops one layer short.
### Client information
Source-level finding verified against upstream `main` at commit `5411f113c`. All platforms; requires a trusted workspace whose `.env` sets the offending variables (untrusted workspaces are protected because `loadEnvironment()` whitelists only `GEMINI_API_KEY`, `GOOGLE_API_KEY`, `GOOGLE_CLOUD_PROJECT`, `GOOGLE_CLOUD_LOCATION`).
### Login information
Not applicable.
### Anything else we need to know?
Sources:
- `packages/cli/src/config/settings.ts:693-726` — trusted workspace `.env` values are loaded into `process.env` unless in `DEFAULT_EXCLUDED_ENV_VARS`
- `packages/cli/src/config/settings.ts:82-87` — `DEFAULT_EXCLUDED_ENV_VARS` contains only 4 entries; no `GIT_*`
- `packages/core/src/utils/gitUtils.ts:9-45` — `getSafeGitEnv()` strips only `GIT_CONFIG_*`/`GIT_CONFIG_PARAMETERS`
- `packages/core/src/services/gitService.ts:102-124` — shadow-repo env built from sanitized `process.env`; execution-affecting `GIT_*` survive
- `packages/cli/src/config/extensions/github.ts:37` — extension clones run with `getSafeGitEnv()`
- Related but distinct: #28684 (env-var load-order race affecting settings resolution) — different defect.
- Duplicate check: searched issues/PRs for "GIT_EXEC_PATH", "GIT_SSH_COMMAND", "safe git env" — no existing report or fix found.
Contributor guide
Research direction
Start with getSafeGitEnv() in packages/core/src/utils/gitUtils.ts, then compare the parallel environment handling in packages/core/src/services/gitService.ts and shellExecutionService.prepareExecution. Review DEFAULT_EXCLUDED_ENV_VARS and trusted .env loading in packages/cli/src/config/settings.ts, plus extension cloning in packages/cli/src/config/extensions/github.ts. Done means execution-affecting GIT_* variables from project environments no longer reach internal git operations while ordinary git behavior remains supported.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, typescript
- Domain
- cli, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100