fix(sandbox): harden filesystem boundaries and isolate runtime state - #29214
DavidAPierce merged 45 commits into
Conversation
- Isolate container mounts by replacing full host ~/.gemini directory volume mounts with an ephemeral, read-only settings file mount (:ro). - Filter custom mount paths (SANDBOX_MOUNTS and allowedPaths) to block host home, user configuration, credential files, and environment files. - Sanitize settings passed into the container by stripping hooks, command execution configurations, and API keys. - Redirect core runtime state (chat history, temp directories, project registry) to an ephemeral directory under os.tmpdir() when running in sandbox mode. - Update macOS Seatbelt profiles and builtins to deny writing to ~/.gemini and deny reading credential files. - Restrict custom command and extension loading in FileCommandLoader to trusted folders when folder trust is enabled. - Add comprehensive unit tests covering path filtering, settings sanitization, runtime state redirection, and Seatbelt profile consistency.
|
📊 PR Size: size/XL
|
|
/gemini review |
Summary of ChangesHello, 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 significantly improves the security posture of the sandbox environment by decoupling runtime execution from the host's persistent configuration. It introduces robust path validation, sanitizes configuration files, and redirects runtime state to ephemeral storage, ensuring that sensitive credentials and host settings remain isolated from containerized processes. Highlights
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Code Review
This pull request enhances sandbox isolation and security by preventing sensitive host paths (such as ~/.gemini, .env files, and credential stores) from being mounted into the sandbox container, sanitizing user settings before mounting them as read-only, and redirecting runtime state to an ephemeral directory in sandbox mode. Feedback on these changes highlights a critical issue where mounting the sanitized settings file to the host container settings path could fail or cause an EISDIR error due to missing parent directories inside the container; updating the container's HOME environment variable and mounting only to /home/node/.gemini/settings.json:ro is recommended. Additionally, a style guide violation was identified in the storage tests, where process.env is modified directly instead of using vi.stubEnv and vi.unstubAllEnvs().
There was a problem hiding this comment.
Code Review
This pull request enhances sandbox security and isolation for the Gemini CLI. Key changes include restricting file command loading based on folder trust, updating macOS Seatbelt sandbox profiles to deny write access to ~/.gemini and block reading sensitive credential or environment files, and sanitizing user settings (removing hooks, tool commands, and API keys) before mounting them as read-only into the sandbox container. Additionally, sensitive host paths are blocked from being mounted, and global runtime directories are redirected to an ephemeral temp directory when running in sandbox mode. There are no review comments provided, so I have no feedback to address.
…s mount path - Set container HOME environment variable explicitly to /home/node to decouple from host home path. - Mount sanitized settings exclusively to /home/node/.gemini/settings.json:ro and remove redundant secondary volume mount. - Update storage sandbox unit tests to use vi.stubEnv and vi.unstubAllEnvs conventions.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request enhances sandbox isolation and security by restricting access to sensitive host configuration files and credentials. Key changes include sanitizing user settings before mounting them into the sandbox, blocking sensitive paths (such as ~/.gemini and .env files) from being mounted or read under macOS Seatbelt and container runtimes, and redirecting global runtime state to an ephemeral directory in sandbox mode. Feedback on these changes highlights several critical security and functional improvements: using resolveToRealPath to prevent symlink-based path traversal bypasses, ensuring macOS Seatbelt profiles also deny reading google_accounts.json and trusted_hooks.json, updating remaining static getters in Storage to use the isolated runtime directory to avoid permission crashes, and refining the folder trust check in FileCommandLoader so that global user commands are not mistakenly blocked.
Note: Security Review did not run due to the size of the PR.
…mmands, and complete storage runtime isolation - Use resolveToRealPath in isSensitiveHostPath to resolve symbolic links across candidate paths and user home directories. - Allow global user commands in listAvailableFiles when workspace folder trust is untrusted. - Add explicit deny file-read rules for google_accounts.json and trusted_hooks.json in macOS Seatbelt profiles and builtins. - Route remaining Storage credential getters to isolated runtime directory in sandbox mode.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request enhances sandbox security and isolation by preventing the container from accessing or mutating sensitive host configurations and credentials. Key changes include sanitizing user settings before mounting them read-only, blocking sensitive host paths from being mounted, updating macOS Seatbelt profiles to deny access to sensitive files, and redirecting runtime state to an isolated directory under the temporary directory in sandbox mode. Feedback was provided to simplify the redundant path resolution logic in isSensitiveHostPath where both homedir() and os.homedir() are resolved and checked separately.
…y checks - Remove redundant osHome and osGeminiDir lookups by leveraging core homedir wrapper. - Simplify home and geminiDir path normalization and realpath resolution. - Maintain single-path equality and subpath checks for container mount filtering.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements comprehensive sandbox isolation and security hardening for the Gemini CLI. It prevents sandbox environments from accessing or mutating sensitive host configurations and credentials by sanitizing settings.json (stripping API keys, tokens, and execution hooks) and mounting it as read-only, rather than mounting the entire ~/.gemini directory. Additionally, it introduces validation to block sensitive host paths from being mounted, updates macOS Seatbelt profiles to explicitly deny access to sensitive files, and redirects global runtime state to an isolated directory under os.tmpdir() when running in sandbox mode. There are no review comments, and the implementation is clean and well-tested; therefore, I have no feedback to provide.
…system-isolation # Conflicts: # packages/cli/src/utils/sandbox.ts # packages/cli/src/utils/sandboxUtils.test.ts # packages/cli/src/utils/sandboxUtils.ts
…ion' into harden-sandbox-filesystem-isolation
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request significantly hardens sandbox security by preventing sensitive host paths (such as the user's home directory, ~/.gemini, credentials, and .env files) from being mounted into the sandbox. Instead of mounting the entire ~/.gemini directory, the sandbox now reads, sanitizes (stripping hooks, API keys, and tokens), and mounts only the sanitized settings.json file as read-only. Additionally, macOS Seatbelt profiles have been updated to explicitly deny read/write access to these sensitive paths. Feedback on these changes identifies two key issues: first, the redirection of runtime operations to a predictable directory under /tmp in sandbox mode is vulnerable to symlink attacks and should use fs.mkdtempSync(); second, isSensitiveHostPath should gracefully handle cases where homedir() returns an empty string to avoid incorrectly resolving to the current working directory and blocking legitimate project mounts.
…homedir - Generate unique temporary directory via mkdtempSync in Storage.getGlobalRuntimeDir for sandbox mode. - Cache runtime directory per session with graceful fallback and testing reset hook. - Check for empty or undetermined homedir in isSensitiveHostPath to prevent resolving to current working directory. - Add comprehensive test coverage for sandbox runtime temporary directory and empty homedir resolution.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request enhances the security and robustness of the Gemini CLI sandbox environment by preventing the mounting of sensitive host paths (such as ~/.gemini, home directories, and credentials) and sanitizing user settings to strip API keys and execution hooks. Additionally, it routes global runtime state to a dedicated cache directory under macOS Seatbelt to ensure persistence, and improves robustness when the home directory is empty. The reviewer recommended replacing synchronous file system operations (fs.existsSync and fs.mkdirSync) in the runtime directory resolution with asynchronous alternatives to avoid blocking the event loop.
…nous directory creation in storage module - Remove synchronous fs.existsSync and fs.mkdirSync from Storage.getGlobalRuntimeDir to avoid blocking the event loop. - Introduce asynchronous Storage.ensureGlobalRuntimeDirExists using fs.promises.mkdir and invoke during Storage.initialize. - Ensure parent directories are created asynchronously prior to writing credentials in MCPOAuthTokenStorage. - Pre-create seatbelt cache directory in sandbox launcher before process initialization. - Update storage unit test suite to verify asynchronous directory creation and non-blocking path resolution.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request significantly hardens sandbox security and isolation by restricting host mounts of sensitive directories (such as ~/.gemini and the user's home directory) and credential files, sanitizing user settings before mounting, and updating macOS Seatbelt profiles to deny unauthorized read/write access. It also routes global runtime state to a persistent cache directory under macOS Seatbelt. Feedback on the changes highlights a critical security vulnerability where the working directory (workdir) is mounted directly into the sandbox without sensitivity checks, which could lead to a sandbox escape if executed from a sensitive host directory.
Note: Security Review did not run due to the size of the PR.
… before mounting - Prevent mounting sensitive host directories such as home or configuration paths as sandbox working directory.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces robust security enhancements to the sandbox environment by preventing sensitive host paths (such as ~/.gemini, user home directories, and credential files) from being mounted or accessed. It implements path sanitization, restricts macOS Seatbelt profiles to block unauthorized read/write access to sensitive files, and routes global runtime state to a dedicated cache directory under macOS Seatbelt. A critical security issue was identified in packages/cli/src/utils/sandboxUtils.ts where the catch block silently ignores all errors from fs.lstatSync and fs.readlinkSync. This could lead to a security bypass if a sensitive symlink fails to resolve due to permission issues (EACCES) rather than the path not existing (ENOENT). It is recommended to only catch and ignore ENOENT errors and rethrow others.
Note: Security Review did not run due to the size of the PR.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request hardens the sandbox security of the Gemini CLI by preventing configuration poisoning and credential leakage. It restricts mounting sensitive host paths (such as user home directories, the .gemini root directory, .env files, and credential files) into the sandbox, and introduces a settings sanitization mechanism that strips API keys, hooks, and custom tool commands. macOS Seatbelt profiles are updated to deny read/write access to sensitive files while allowing read-only access to non-sensitive configurations, and global runtime state is routed to a permitted persistent cache directory (~/.cache/.gemini) under macOS Seatbelt. Robust tests are added to validate these security boundaries and path resolution behaviors. I have no feedback to provide as there are no review comments.
Note: Security Review did not run due to the size of the PR.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces significant security enhancements to the sandbox environment across Docker, macOS Seatbelt, and LXC runtimes. It prevents sensitive host paths (such as ~/.gemini, home directories, .env files, and credential stores) from being mounted or accessed inside the sandbox, and sanitizes user settings before mounting them. Additionally, it routes global runtime state to a dedicated persistent cache directory under macOS Seatbelt to comply with sandbox restrictions. The feedback suggests using the robust resolveToRealPath utility in packages/cli/src/utils/sandbox.ts to ensure consistent path resolution and error handling when validating workspace directories.
There was a problem hiding this comment.
Code Review
This pull request significantly hardens the sandbox security model for the Gemini CLI. It prevents sensitive host paths (such as the user's home directory, .gemini configuration folder, API keys, and .env files) from being mounted or accessed within the sandbox container or macOS Seatbelt environment. It introduces settings sanitization to strip execution hooks and credentials before mounting, restricts sandbox execution from sensitive host directories, and routes global runtime state to a dedicated cache directory under macOS Seatbelt to comply with write restrictions. Additionally, it refactors the file command loader to allow global user commands while safely excluding project and extension commands when folder trust is disabled. I have no further feedback to provide as the implementation is robust and well-tested.
Note: Security Review did not run due to the size of the PR.
|
done |
Summary
Hardens sandbox filesystem boundaries by isolating sandbox runtime state from host configuration directories, replacing host directory mounts with sanitized configuration files, standardizing on realpath resolution during path sensitivity checks with non-existent path existence fallbacks, validating working directory paths against sensitive host locations prior to mounting across container runtimes, LXC, and macOS Seatbelt, restricting ephemeral directory permissions synchronously with non-POSIX error tolerance, aligning core storage runtime directory with launcher-managed ephemeral volumes and macOS Seatbelt temporary directories, decoupling the container environment, and enforcing stricter path validation across container runtimes and macOS Seatbelt profiles.
Details
When running under sandbox environments (
--sandboxwith Docker, Podman, runsc, LXC, orsandbox-exec), runtime execution should be strictly decoupled from the host user's persistent configuration and credentials. This PR implements the following structural improvements:Working Directory & Container Mount Validation:
workdirandtargetDir) usingisSensitiveHostPath(), strictly prohibiting launching container, LXC, or macOS Seatbelt sandboxes from sensitive host directories (such as the user's home directory root, root filesystem, or~/.gemini).~/.gemini), host home directory, or any of their parent directories (e.g./home,/) into containers.isSensitiveHostPath()insandboxUtils.tsto directly resolve candidate paths (rawHome,expandedPath,geminiDirCandidate) viaresolveToRealPath()without pre-checking existence on disk.safeResolveToRealPath()to traverse parent segments and inspect symlink targets usingfs.lstatSyncandfs.readlinkSyncwhen files or targets do not exist on disk, preventing broken symlinks from bypassing sensitivity checks while tracking visited paths with platform-specific case-insensitivity on Windows to catch circular symlinks and fail closed. Normalized symlink path matching in unit test mocks to ensure cross-platform Windows compatibility.isSensitiveHostPath()usingSENSITIVE_SETTINGS_FILENAMES(includingtrustedFolders.jsonandpolicy_integrity.json), preventing exposure of folder trust decisions and policy integrity state to sandbox containers without list duplication.arePathsEqual()andisSubpathOf()across operating systems usingpath.resolve()and platform-specific path separators.homedir()is empty or undetermined to prevent resolving toprocess.cwd()and blocking valid workspace project mounts.SANDBOX_MOUNTS(rejects sensitive paths withFatalSandboxError) andconfig.allowedPaths(omits sensitive paths with a log warning).Ephemeral Directory Permissions & Settings Sanitization:
0o700) immediately upon creation via synchronousfs.chmodSync()wrapped in non-POSIX error handling to silently tolerate platforms that do not support POSIX permission bits.sandbox.tsto only attempt loadingsettings.jsonwhenhomedir()is present and truthy, cleanly bypassing ephemeral directory creation and redundant disk I/O when the home directory is undefined.isRecordtype guard insandbox.tsandsandboxUtils.tsto explicitly exclude arrays (!Array.isArray(value)), ensuring robust type validation and simplifying JSON settings parsing.sanitizeSettingsForSandbox()to produce a sanitized copy of user settings stripped of hooks, custom tool commands (callCommand,discoveryCommand), and API keys.0o600(from0o444) to prevent deletion failures during temporary directory cleanup on Windows hosts.--env HOME=/home/nodeacross container runs./home/node/.gemini:rwinside the container, ensuring Docker automatically establishes parent directory structure on custom images while keeping host user directories fully isolated.Runtime State Redirection & Core Storage Lifecycle Alignment:
sandbox-exec), where file writes toHOME_DIR/.geminiare explicitly blocked by the Seatbelt profile, updatedStorage.getGlobalRuntimeDir()in@google/gemini-cli-coreto route runtime state to the user's persistent cache directory (path.join(homedir(), '.cache', GEMINI_DIR)) as a non-blocking path resolver without synchronous filesystem calls (fs.existsSync,fs.mkdirSync), ensuring history and session state persist across CLI invocations while keeping the event loop unblocked. Introduced asynchronousStorage.ensureGlobalRuntimeDirExists()(invoked duringStorage.initialize()) and ensured asynchronous parent directory creation inMCPOAuthTokenStoragebefore credential writes.Storage.getPolicyIntegrityStoragePath()to resolve underStorage.getGlobalRuntimeDir(), ensuring policy integrity records are preserved in runtime cache directories during sandboxed execution.Storage.isWorkspaceHomeDir()against empty or undefinedhomedir()values and path resolution errors, ensuring reliable execution in minimal container environments without unhandled exceptions./home/node/.gemini), ensuring session runtime files (history,tokens,tmp,projects.json) persist cleanly throughout the session and are managed by the container lifecycle.Storageby eliminating redundant nested temporary directory creation, listener hooks, and manual directory resets.Dockerfileto initialize a container-internal.geminidirectory owned bynode:node.macOS Seatbelt Policy Hardening:
INCLUDE_DIR_*parameters.HOME_DIR/.geminifrom(allow file-write*)in all Seatbelt profiles (sandbox-macos-*.sb) and builtin profile definitions.(deny file-write*)rules forHOME_DIR/.geminias well as filesystem-wide regex write denials for sensitive configuration and credential files (trustedFolders.json,policy_integrity.json,trusted_hooks.json,oauth_creds.json,.env, etc.).(deny file-read*)rules for credential stores, account profiles, hook definitions, and configuration files (trustedFolders.json,policy_integrity.json,google_accounts.json,trusted_hooks.json,oauth_creds.json, etc.), and environment files.sandbox-macos-strict-*.sband builtin definitions) by removing broad~/.geminisubpath read access while explicitly allowing onlysettings.jsonandkeybindings.jsonconfiguration files.Folder Trust Refinement for Commands:
FileCommandLoader.tsto allow global user commands (~/.gemini/commands) to remain accessible even in untrusted workspaces, removing early returns inloadCommands()and relying on directory-level trust filtering to restrict workspace and extension commands.Related Issues
Resolves configuration isolation and credential exposure across sandbox runtime boundaries.
How to Validate
Run the targeted test suites:
Expected output: All test suites pass with 0 errors and 0 lint warnings.
Pre-Merge Checklist