Skip to content

fix(cli): isolate settings directory in sandbox containers - #29216

Open
jvargassanchez-dot wants to merge 4 commits into
google-gemini:mainfrom
jvargassanchez-dot:fix/isolate-sandbox-settings
Open

fix(cli): isolate settings directory in sandbox containers#29216
jvargassanchez-dot wants to merge 4 commits into
google-gemini:mainfrom
jvargassanchez-dot:fix/isolate-sandbox-settings

Conversation

@jvargassanchez-dot

Copy link
Copy Markdown
Contributor

Summary

When running Gemini CLI inside a container sandbox (Docker/Podman), the user configuration directory was previously mounted directly from the host's ~/.gemini folder. This could inadvertently expose sensitive local credentials (such as OAuth tokens, account credentials, and authentication stores) within the container's execution boundary.

This change isolates the mounted settings directory by creating a session-scoped temporary directory on the host that includes only non-sensitive configuration (e.g., settings.json, custom commands, skills, policies, keybindings, and trusted folders) while strictly omitting credential stores. The temporary settings directory is automatically cleaned up upon container process completion.

Details

  1. Selective Settings Copy & Credential Omission:

    • Introduced prepareIsolatedSettingsDir and isCredentialOrSensitivePath in packages/cli/src/utils/sandboxUtils.ts.
    • Explicitly redacts known credential and token stores (oauth_creds.json, google_accounts.json, gemini-credentials.json, mcp-oauth-tokens.json, a2a-oauth-tokens.json, and wildcard credential/token patterns).
    • Omits session history and cache stores (history/, tmp/, bin/).
    • Copies user configuration files and directories (settings.json, commands/, skills/, policies/, keybindings.json, trustedFolders.json).
  2. Container Volume Isolation:

    • Updated packages/cli/src/utils/sandbox.ts to mount the isolated directory into /home/node/.gemini (and the containerized host settings mirror) instead of mounting the host's raw .gemini directory.
    • Retained all existing functional volume mounts (workspace workdir, temp directory, ~/.config/gcloud, and GOOGLE_APPLICATION_CREDENTIALS for Vertex AI ADC workflows).
  3. Lifecycle Cleanup:

    • Registered isolatedSettingsDir for automatic cleanup via fs.rmSync in the cleanup() handler executed on process exit, SIGINT, and SIGTERM.
  4. Testing:

    • Added unit test in packages/cli/src/utils/sandbox.test.ts verifying that start_sandbox mounts the isolated directory and does not mount raw host .gemini folders.
    • Added unit test suite in packages/cli/src/utils/sandboxUtils.test.ts testing credential identification and isolation logic.

Related Issues

How to Validate

  1. Run the sandbox unit tests:
    npm test -w @google/gemini-cli -- src/utils/sandbox.test.ts
    npm test -w @google/gemini-cli -- src/utils/sandboxUtils.test.ts
    Expected result: All tests pass, verifying that the container receives an isolated settings directory without credential files.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • [ x] Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • [ x] Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • [ x] Linux
      • [ x] npm run
      • npx
      • [ x] Docker

Isolate the user settings directory mounted into sandbox containers by
preparing a session-scoped temporary directory containing only
non-sensitive configuration (settings, commands, skills, policies,
keybindings, and trusted folders) while explicitly omitting credentials
and authentication token stores.

Clean up the temporary directory upon container exit.
@jvargassanchez-dot
jvargassanchez-dot requested a review from a team as a code owner September 4, 2026 22:38
@github-actions github-actions Bot added the size/l A large sized PR label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

📊 PR Size: size/L

  • Lines changed: 322
  • Additions: +317
  • Deletions: -5
  • Files changed: 4

@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 improves the security posture of the Gemini CLI by isolating the user configuration directory when running in containerized sandboxes. By creating a temporary, filtered copy of the settings directory, the CLI ensures that sensitive credentials and session data remain on the host while still providing the container with necessary configuration files. This change mitigates the risk of credential leakage within untrusted execution environments.

Highlights

  • Security Enhancement: Implemented an isolated settings directory for sandbox containers to prevent the accidental exposure of sensitive local credentials and authentication tokens.
  • Selective Synchronization: Added logic to copy only non-sensitive configuration files (e.g., settings.json, commands, skills) into a temporary session-scoped directory while explicitly redacting credential stores and cache directories.
  • Lifecycle Management: Ensured the temporary isolated settings directory is automatically cleaned up upon container process completion, SIGINT, or SIGTERM.
  • Testing: Added comprehensive unit tests to verify the credential identification logic and confirm that the sandbox correctly mounts the isolated directory instead of the raw host settings folder.
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.

@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 enhances sandbox security by mounting an isolated settings directory instead of exposing raw host credentials directly to the container. It introduces helper functions to identify and filter out sensitive files (such as OAuth credentials, history, and temp files) before copying the configuration to a temporary directory. Feedback was provided regarding the sensitivity check base.includes('credential'), which is too broad and may cause false positives on user-defined scripts or files containing the word "credential" in their name. A more precise suffix-matching approach was suggested.

Comment thread packages/cli/src/utils/sandboxUtils.ts
@gemini-cli gemini-cli Bot added the status/need-issue Pull requests that need to have an associated issue. label Sep 4, 2026
Use precise suffix matching for credential and token stores to prevent
false positives on user scripts and utilities containing 'credential'
in their filenames.
@jvargassanchez-dot

Copy link
Copy Markdown
Contributor Author

/gemini review

@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 introduces an isolated settings directory for the Docker sandbox to prevent exposing sensitive host credentials (such as OAuth tokens and account files) to untrusted containers. It filters out sensitive files and directories when copying settings to a temporary location. The review feedback correctly identifies a bug where generic directory names like 'history', 'tmp', and 'bin' are filtered out recursively, which would cause false positives for nested directories (e.g., within user commands or skills). The reviewer suggests restricting these generic exclusions to direct children of the root settings directory and adding corresponding unit tests.

Comment thread packages/cli/src/utils/sandboxUtils.ts Outdated
Comment thread packages/cli/src/utils/sandboxUtils.test.ts
…ctory

Ensure that directory names such as 'history', 'tmp', and 'bin' are only
excluded when they are direct children of the root settings directory,
preventing false positives on nested directories within custom commands
or skills.
@jvargassanchez-dot

Copy link
Copy Markdown
Contributor Author

/gemini review

@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 enhances sandbox security by mounting an isolated settings directory instead of directly exposing host credentials to the Docker container. It introduces a filtering mechanism to exclude sensitive files (such as OAuth tokens and history) when copying settings to a temporary directory, along with corresponding tests. The review feedback recommends expanding this filter to cover other common sensitive file patterns like .env, .key, .pem, and key.json, and updating the unit tests accordingly.

Comment thread packages/cli/src/utils/sandboxUtils.ts
Comment thread packages/cli/src/utils/sandboxUtils.test.ts
@jvargassanchez-dot

Copy link
Copy Markdown
Contributor Author

/gemini review

@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 enhances sandbox security by copying user settings to an isolated temporary directory and filtering out sensitive credential files before mounting them into the Docker container. The review feedback highlights a security concern regarding world-readable permissions on the created temporary directory, suggesting restricting it to 0700. It also recommends expanding the credential-filtering logic to cover singular variations of sensitive filenames (such as token and cred.json) and adding corresponding unit tests.

Comment on lines +86 to +88
const isolatedDir = fs.mkdtempSync(
path.join(baseTmpDir, 'gemini-sandbox-settings-'),
);

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.

security-high high

The temporary directory isolatedDir is created in a shared location (e.g., /tmp) using fs.mkdtempSync without restricting its permissions. By default, this directory is created with world-readable permissions (usually 0755 or 0775 depending on the process's umask). Since this directory is populated with copied user settings (which can contain sensitive custom commands, skills, policies, keybindings, and trusted folders), any other local user on the same machine can read these files. To prevent local information disclosure, restrict the permissions of the temporary directory immediately after creation to 0700 (read, write, and execute by the owner only) using fs.chmodSync(isolatedDir, 0o700).

Suggested change
const isolatedDir = fs.mkdtempSync(
path.join(baseTmpDir, 'gemini-sandbox-settings-'),
);
const isolatedDir = fs.mkdtempSync(
path.join(baseTmpDir, 'gemini-sandbox-settings-'),
);
fs.chmodSync(isolatedDir, 0o700);
References
  1. When creating temporary files or directories in global temporary directories (e.g., /tmp), use fs.mkdtempSync() to generate securely named, uniquely named temporary directories. This mitigates symlink attacks where an attacker could pre-create a symlink with a predictable name to truncate arbitrary files.

Comment on lines +60 to +72
if (
base.endsWith('.credentials') ||
base.endsWith('credentials') ||
base.endsWith('credentials.json') ||
base.endsWith('tokens.json') ||
base.endsWith('creds.json') ||
base === '.env' ||
base.endsWith('.env') ||
base.endsWith('.key') ||
base.endsWith('.pem') ||
base.endsWith('.p12') ||
base.endsWith('key.json')
) {

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.

high

The current implementation checks for plural tokens.json and creds.json but misses singular variations like token.json, token, and cred.json. This could lead to sensitive credentials or OAuth tokens being leaked into the sandbox container if they are named using singular forms (e.g., github-token.json or user_cred.json). Adding singular forms to the check improves security coverage.

  if (
    base.endsWith('.credentials') ||
    base.endsWith('credentials') ||
    base.endsWith('credentials.json') ||
    base.endsWith('tokens.json') ||
    base.endsWith('token.json') ||
    base.endsWith('token') ||
    base.endsWith('creds.json') ||
    base.endsWith('cred.json') ||
    base === '.env' ||
    base.endsWith('.env') ||
    base.endsWith('.key') ||
    base.endsWith('.pem') ||
    base.endsWith('.p12') ||
    base.endsWith('key.json')
  ) {

Comment on lines +283 to +292
it('should identify tokens, credentials, and sensitive directories', () => {
expect(
isCredentialOrSensitivePath('/home/user/.gemini/custom-tokens.json'),
).toBe(true);
expect(
isCredentialOrSensitivePath('/home/user/.gemini/api.credentials'),
).toBe(true);
expect(
isCredentialOrSensitivePath('/home/user/.gemini/user_creds.json'),
).toBe(true);

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.

high

Add test cases to verify that singular variations like token.json, token, and cred.json are correctly identified as sensitive paths.

    it('should identify tokens, credentials, and sensitive directories', () => {
      expect(
        isCredentialOrSensitivePath('/home/user/.gemini/custom-tokens.json'),
      ).toBe(true);
      expect(
        isCredentialOrSensitivePath('/home/user/.gemini/token.json'),
      ).toBe(true);
      expect(
        isCredentialOrSensitivePath('/home/user/.gemini/github-token'),
      ).toBe(true);
      expect(
        isCredentialOrSensitivePath('/home/user/.gemini/api.credentials'),
      ).toBe(true);
      expect(
        isCredentialOrSensitivePath('/home/user/.gemini/user_creds.json'),
      ).toBe(true);
      expect(
        isCredentialOrSensitivePath('/home/user/.gemini/user_cred.json'),
      ).toBe(true);

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

Labels

size/l A large sized PR status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant