Skip to content

fix(cli): stop an untrusted workspace wiping its own settings.json - #29466

Closed
lets-order-some-fries wants to merge 1 commit into
google-gemini:mainfrom
lets-order-some-fries:fix/untrusted-workspace-settings-data-loss
Closed

lets-order-some-fries wants to merge 1 commit into
google-gemini:mainfrom
lets-order-some-fries:fix/untrusted-workspace-settings-data-loss

Conversation

@lets-order-some-fries

Copy link
Copy Markdown

Summary

gemini mcp add run in a folder the user has not trusted silently destroys that
project's .gemini/settings.json
, keeping only the key it just wrote, and reports
success. Untrusted is the default state for any folder not explicitly trusted, so this is
a normal path. Reproduces on the published 0.60.0 and on main @ d5b3e3acc.

Fixes #29465.

Details

Four steps, all in packages/cli/src:

  1. config/settings.ts — an untrusted workspace is replaced by an emptied copy:

    this.workspace = isTrusted ? workspace : this.createEmptyWorkspace(workspace);

    createEmptyWorkspace spreads the original, so the copy keeps the real path and
    leaves readOnly unset.

  2. setValue() resolves through forScope(Workspace), gets that copy, and persists it —
    isPersistable is !settingsFile.readOnly, so undefined means writable.

  3. saveSettings() writes originalSettings — {} plus the one new key — to the real path.

  4. utils/commentJson.ts — applyKeyDiff is sync-by-omission: every key on disk and
    absent from the update is deleted. Correct when originalSettings mirrors the file;
    destructive when the in-memory copy was deliberately emptied.

The fix

createEmptyWorkspace marks the copy readOnly: true. That is the same mechanism
loadSettings already uses
to keep the workspace unwritable when it is the home
directory (readOnly: storage.isWorkspaceHomeDir()), so this is the existing idiom rather
than a new concept. saveSettings has exactly one caller and it is gated on
isPersistable, so this closes the write path completely.

setValue also now warns when a workspace write did not reach disk. Callers such as
mcp add log success immediately after setValue returns, so staying silent reads as
"saved". Only User and Workspace are ever written (nothing in the tree calls
setValue(SettingScope.System…)), so this cannot fire for the intentionally read-only
system scopes.

Deliberately out of scope

What should happen instead of the write is left to you, and tracked in #29465: refuse
outright with "this folder is not trusted", or write through to _workspaceFile so an
explicit mcp add still lands. Both are behaviour decisions on a trust boundary. This PR
only stops the destruction and stops the silence.

One consequence of that: on an untrusted folder mcp add now prints the warning and
then still prints its own success line. Suppressing the second needs a result threaded
back to each of the four callers (mcp add, mcp remove, gemma setup, hooks migrate),
which is the same design decision. The warning is printed first so the user sees it.

Related Issues

Fixes #29465

How to Validate

npx vitest run src/config/settings-untrusted-write.test.ts --root packages/cli

New file, 6 cases, real files in a temp dir rather than fs mocks — what lands on disk is
the whole point. 3 fail without the source change; the others are controls asserting
the trusted path is untouched.

End to end:

npm run bundle
CLI="$PWD/bundle/gemini.js"
export HOME=$(mktemp -d) && mkdir -p "$HOME/.gemini" "$HOME/proj/.gemini"
cat > "$HOME/proj/.gemini/settings.json" <<'JSON'
{
  "ui": { "theme": "GitHub" },
  "context": { "fileName": "GEMINI.md" },
  "mcpServers": { "existing": { "command": "echo" } }
}
JSON
cd "$HOME/proj"
md5 "$HOME/proj/.gemini/settings.json"
node "$CLI" mcp add newsrv echo          # untrusted: the default
md5 "$HOME/proj/.gemini/settings.json"   # must be unchanged

GEMINI_CLI_TRUST_WORKSPACE=true node "$CLI" mcp add newsrv echo   # control
cat "$HOME/proj/.gemini/settings.json"   # merges: existing AND newsrv, ui/context intact

On main the first mcp add leaves only {"mcpServers":{"newsrv":…}} — ui, context
and the pre-existing existing server all gone.

Observed on this branch (macOS, node v22.13.0): md5 identical across the untrusted run,
warning printed, and the trusted control merges correctly.

Suites on this branch: src/config/ + src/commands/mcp/ → 41 files, 956 passed.
Full npm run test -w packages/cli → 466 of 467 files pass; the one failure is
src/gemini.test.tsx, which fails the same 9 tests on clean main in this checkout
(an untrusted-workspace environment issue — I re-ran it on main to confirm the lists are
identical, since two of those nine are trust-related and this change touches trust).
npm run typecheck -w packages/cli and eslint packages/cli/src/config/ both clean.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed) — no user-facing documented
    behaviour changes; the fix restores what the docs already imply
  • Added/updated tests (if needed)
  • Noted breaking changes (if any) — none. Workspace settings in an untrusted folder
    were never meant to persist; today they persist by destroying the file.
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

No platform-specific paths involved; the tests use os.tmpdir() and path.join, so they
run identically on the Windows shard.

🤖 Generated with Claude Code

An untrusted folder gets an emptied copy of its workspace settings, and
createEmptyWorkspace spreads the original, so the copy keeps the real path
and leaves readOnly unset. setValue then persists it, because isPersistable
is !readOnly, and saveSettings writes originalSettings - which is {} plus
whatever was just set - to that real path. updateSettingsFilePreservingFormat
merges via applyKeyDiff, which is sync-by-omission: every key on disk and
absent from the update is deleted.

So `gemini mcp add newsrv echo` in a folder the user has not trusted - the
default state - reduces .gemini/settings.json to a single mcpServers entry,
destroying unrelated settings AND the servers already configured, while
reporting success. Reproduces on published 0.60.0 and on main.

Marks the emptied copy readOnly, which is the same mechanism loadSettings
already uses to keep the workspace unwritable when it is the home directory.
saveSettings has exactly one caller and it is gated on isPersistable, so this
closes the write path.

setValue now also warns when a workspace write did not reach disk. Callers
such as mcp add report success immediately afterwards, so silence reads as
"saved". Only User and Workspace are ever written, so this cannot fire for
the intentionally read-only system scopes.

Tests use real files in a temp dir rather than fs mocks, since what lands on
disk is the whole point; 3 of the 6 fail without the fix.

Fixes google-gemini#29465
@lets-order-some-fries
lets-order-some-fries requested a review from a team as a code owner September 23, 2026 19:00
@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 critical issue where the CLI would silently destroy a project's settings.json file when running commands like 'gemini mcp add' in an untrusted directory. By marking the in-memory representation of an untrusted workspace as read-only, the system now correctly prevents destructive write operations. Additionally, the CLI now provides explicit feedback to the user when a write operation is blocked, preventing the false impression of success.

Highlights

  • Prevent Data Loss: Updated the workspace creation logic to mark empty workspaces as read-only, preventing the CLI from accidentally overwriting and destroying existing settings files in untrusted directories.
  • Improved User Feedback: Added a warning notification when a workspace setting write fails due to lack of trust, ensuring users are aware that their changes were not persisted.
  • Validation: Introduced a new test suite that verifies settings file integrity in untrusted workspaces, ensuring existing keys are preserved and writes are blocked.
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. ↩

@github-actions github-actions Bot added the size/m A medium sized PR label Sep 23, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/M

  • Lines changed: 155
  • Additions: +155
  • Deletions: -0
  • Files changed: 2

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request ensures that workspace settings are not silently discarded when a workspace is untrusted or unwritable. It marks the emptied workspace settings object as read-only to prevent it from being persisted back to disk, and emits a warning feedback event when a write is attempted under these conditions. A new test suite has been added to verify these settings write behaviors. There are no review comments, and I have no feedback to provide.

@gemini-cli gemini-cli Bot added area/core Issues related to User Interface, OS Support, Core Functionality priority/p1 Important and should be addressed in the near term. labels Sep 23, 2026
@gemini-cli

gemini-cli Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Hi there! Thank you for your interest in contributing to Gemini CLI.

To ensure we maintain high code quality and focus on our prioritized roadmap, we only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.

This PR will be closed in 7 days if it remains without that designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

@gemini-cli

gemini-cli Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

This pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality priority/p1 Important and should be addressed in the near term. size/m A medium sized PR status/pr-nudge-sent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gemini mcp add in an untrusted folder silently wipes the project's settings.json

1 participant