Skip to content

feat(sandbox): implement native bubblewrap (bwrap) sandbox for Linux#22399

Closed
Lyonk71 wants to merge 11 commits intogoogle-gemini:mainfrom
Lyonk71:feat/bwrap-sandbox-v2
Closed

feat(sandbox): implement native bubblewrap (bwrap) sandbox for Linux#22399
Lyonk71 wants to merge 11 commits intogoogle-gemini:mainfrom
Lyonk71:feat/bwrap-sandbox-v2

Conversation

@Lyonk71
Copy link
Copy Markdown
Contributor

@Lyonk71 Lyonk71 commented Mar 14, 2026

Summary

This PR implements a native, high-performance Linux sandbox using bubblewrap (bwrap). It provides a hardened, zero-dependency environment for Linux distributions (like Fedora), replacing the need for rootless Podman/Docker for local isolation.

Supersedes #22380, rebased on top of #22231 (SandboxManager integration).

Details

  • Two-Layer Isolation: Hardens both the main CLI process (outer sandbox) and individual tool executions (nested sandbox via BwrapSandboxManager).
  • Clean Architecture: Integrates with the injectable SandboxManager interface — zero changes to shellExecutionService.ts. The BwrapSandboxManager is automatically selected via createSandboxManager() when running inside a bwrap outer sandbox.
  • Kernel Namespaces: Leverages user, PID, network (optional), IPC, UTS, and cgroup namespaces for robust process isolation.
  • Strict Filesystem Allow-list: Only maps essential system paths (/usr, /bin, /lib, etc.) and the project workspace using a "Skeleton" mount approach.
  • Credential Protection: Physically hides sensitive host directories (~/.ssh, ~/.aws, etc.) by masking them with tmpfs or omitting them from binds.
  • Nested Tool Lockdown: Shell tools are strictly isolated with Network OFF by default and private /tmp, /run/user, and /dev/shm mounts to prevent host service escapes (D-Bus, Wayland, X11).
  • Environment Sanitization: Implements a strict allow-list for environment variables passed into the sandbox.
  • Distro Compatibility: Robust detection for NVM/Asdf Node.js paths and varied Linux filesystem layouts (Fedora, Debian, Arch, etc.).

How to Validate

  1. Run GEMINI_SANDBOX=bwrap npm run start -- -s.
  2. Verify isolation in the chat:
    • ls ~/.ssh -> should fail or be empty.
    • curl https://google.com -> should fail in tools (nested --unshare-net).
    • ls /tmp -> should be a private, empty tmpfs.
    • Web search -> should succeed (CLI network working).
  3. Run sandbox config tests: cd packages/cli && npx vitest run src/config/sandboxConfig.test.ts

Pre-Merge Checklist

  • Added/updated tests
  • Noted breaking changes (None - opt-in feature)
  • Validated on Linux (Fedora 43)

Implement a lightweight, non-container sandbox using bubblewrap for Linux.
The outer sandbox wraps the entire CLI process with namespace isolation,
while BwrapSandboxManager provides nested per-tool isolation (network
disabled) through the injectable SandboxManager interface — requiring
zero changes to shellExecutionService.
@Lyonk71 Lyonk71 requested review from a team as code owners March 14, 2026 02:36
@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 introduces a significant enhancement to the sandboxing capabilities on Linux by integrating bubblewrap (bwrap). This provides a more secure and efficient native sandboxing solution, moving away from container-based approaches like Docker or Podman for local isolation. The change aims to offer a hardened execution environment for CLI processes and nested tools, improving security and compatibility across various Linux distributions without external dependencies.

Highlights

  • Native Bubblewrap (bwrap) Sandbox for Linux: Implemented a native, high-performance Linux sandbox using bubblewrap (bwrap) to provide a hardened, zero-dependency environment, serving as an alternative to rootless Podman/Docker for local isolation.
  • Two-Layer Isolation: Introduced a two-layer isolation model, hardening both the main CLI process (outer sandbox) and individual tool executions (nested sandbox via BwrapSandboxManager).
  • Clean Architecture Integration: Integrated the new BwrapSandboxManager with the injectable SandboxManager interface, ensuring zero changes to shellExecutionService.ts and automatic selection when running inside a bwrap outer sandbox.
  • Robust Process Isolation: Leveraged various kernel namespaces (user, PID, network, IPC, UTS, cgroup) for robust process isolation within the sandbox.
  • Strict Filesystem and Credential Protection: Implemented a strict filesystem allow-list using a 'Skeleton' mount approach, mapping only essential system paths and the project workspace, and physically hiding sensitive host directories like ~/.ssh.
  • Nested Tool Lockdown: Ensured shell tools are strictly isolated with network access turned OFF by default and private /tmp, /run/user, and /dev/shm mounts to prevent host service escapes.
  • Environment Sanitization and Distro Compatibility: Included strict allow-listing for environment variables and robust detection for NVM/Asdf Node.js paths and varied Linux filesystem layouts (Fedora, Debian, Arch, etc.).
Changelog
  • package-lock.json
    • Updated dependency metadata by removing the 'peer: true' flag from several packages.
  • packages/cli/src/config/sandboxConfig.test.ts
    • Updated sandbox command validation to include 'bwrap'.
    • Adjusted default 'networkAccess' for 'sandbox-exec' to true in tests.
    • Added a test case to verify 'bwrap' is used when available on Linux and sandboxing is enabled.
    • Updated error message for missing sandbox commands to include 'bwrap'.
  • packages/cli/src/config/sandboxConfig.ts
    • Added 'bwrap' to the list of valid sandbox commands.
    • Modified the sandbox command auto-detection logic to prioritize 'bwrap' on Linux when sandboxing is enabled.
    • Updated the error message for failing to determine a sandbox command to include 'bwrap'.
    • Set default 'networkAccess' to true for 'bwrap' and 'sandbox-exec' commands if not explicitly configured.
  • packages/cli/src/config/settingsSchema.ts
    • Extended the 'command' enum in the settings schema to include 'bwrap' as a valid sandbox option.
  • packages/cli/src/utils/sandbox.ts
    • Integrated the new 'start_bwrap_sandbox' function into the main 'start_sandbox' dispatcher.
    • Added 'start_bwrap_sandbox' function to implement bubblewrap-specific sandboxing logic, including namespace unsharing, filesystem binding, environment sanitization, and command execution.
  • packages/cli/src/utils/sandboxUtils.ts
    • Updated the 'shouldUseCurrentUserInSandbox' utility to include Fedora in the auto-detection for Linux distributions that default to using the current user's UID/GID.
  • packages/core/src/config/config.ts
    • Updated the 'SandboxConfig' interface and 'ConfigSchema' to include 'bwrap' as a possible value for the 'command' property.
  • packages/core/src/services/bwrapSandboxManager.ts
    • Added a new file defining the 'BwrapSandboxManager' class, which provides nested tool-level isolation using bubblewrap on Linux.
    • Implemented 'prepareCommand' method within 'BwrapSandboxManager' to construct 'bwrap' commands with strict filesystem mounts, network isolation, and environment sanitization for tools.
  • packages/core/src/services/sandboxManager.ts
    • Imported 'BwrapSandboxManager'.
    • Modified 'createSandboxManager' to return an instance of 'BwrapSandboxManager' if the 'SANDBOX' environment variable is set to 'bwrap'.
  • schemas/settings.schema.json
    • Updated the JSON schema for sandbox settings to include 'bwrap' as an allowed value for the 'command' property.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

Copy link
Copy Markdown
Contributor

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

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 a native Linux sandbox using bubblewrap (bwrap) to provide a two-layer isolation model for the main CLI process and individual tool executions. However, a critical command injection vulnerability has been identified in the 'outer' sandbox implementation in packages/cli/src/utils/sandbox.ts due to improper handling of nodeOptions during command string construction, which could lead to arbitrary command execution within the sandbox. Additionally, there is a path traversal vulnerability that could allow a sandbox escape by mounting sensitive system directories, and missing propagation of authentication credentials (API keys and ADC) into the sandbox, which would prevent the CLI from functioning correctly. Addressing these issues is crucial for the security and functionality of this otherwise strong addition to the project's security posture.

Comment on lines +1302 to +1308
const innerCmd = [
'SANDBOX=bwrap',
`NODE_OPTIONS="${nodeOptions}"`,
...finalArgv.map((arg) => quote([arg])),
].join(' ');

const args = [...bwrapArgs, '--', shell, '-c', innerCmd];
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 innerCmd string is constructed by concatenating nodeOptions into a shell command without proper quoting or sanitization. Specifically, nodeOptions is embedded within double quotes at line 1304. If any argument in nodeArgs (which populates nodeOptions) contains a double quote, it can break out of the environment variable assignment and execute arbitrary shell commands inside the sandbox. Although this execution occurs within the sandbox, it still represents a command injection vulnerability that could be exploited if nodeArgs are influenced by untrusted sources (e.g., project-specific configurations).

To remediate this, avoid constructing a shell command string and using shell -c. Instead, pass the environment variables directly to the spawn call via the env option, or use bwrap's --setenv flag to set environment variables for the sandboxed process. If a shell must be used, ensure all components of the command string are properly quoted using a library like shell-quote.

References
  1. This rule emphasizes the need to sanitize environments to prevent bypasses or injections, which is directly relevant to the nodeOptions injection issue.
  2. This rule also highlights the importance of sanitizing environments to prevent malicious actions, reinforcing the need for proper handling of nodeOptions to avoid command injection.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gemini-code-assist - updates pushed. Does this look correct now?

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.

Yes, the updates look correct now. You've addressed the command injection vulnerability by:

  1. Using bwrap's --setenv flag to pass NODE_OPTIONS directly to the sandboxed process, avoiding shell interpolation for this sensitive environment variable.
  2. Employing the shell-quote library's quote function to properly escape cliArgs when constructing the innerCmd string, which is then passed to shell -c.

These changes significantly improve the security of the sandbox by preventing malicious input in nodeOptions or cliArgs from executing arbitrary commands.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Thank you for the detailed explanation regarding the --unshare-user namespace and how it mitigates symlink/path traversal concerns. The clarification that the kernel enforces host's DAC permissions even for unprivileged users within the namespace, and your verification of this behavior, is very helpful. This addresses the potential security consideration effectively.

Lyonk71 added 2 commits March 13, 2026 23:27
Add GEMINI_API_KEY, GOOGLE_API_KEY, GOOGLE_APPLICATION_CREDENTIALS,
and other Gemini/Google env vars to the safeEnvKeys allowlist, matching
parity with the Docker/Podman sandbox. Also mount the ADC file
read-only when GOOGLE_APPLICATION_CREDENTIALS is set.
Replace shell string interpolation of SANDBOX and NODE_OPTIONS with
bwrap's --setenv flag, eliminating any theoretical command injection
via crafted nodeOptions values.
@Lyonk71 Lyonk71 force-pushed the feat/bwrap-sandbox-v2 branch from c39be78 to 3bf9782 Compare March 14, 2026 03:44
Lyonk71 added 6 commits March 13, 2026 23:46
Move duplicated bwrap namespace flags, path binding loops, and node
binary binding logic into bwrapUtils.ts. The security-relevant
differences (network isolation, writable mounts, env filtering) stay
inline in each sandbox.
Cover namespace isolation flags, path binding helpers, node binary
binding, environment sanitization, network isolation, and non-Linux
fallback behavior.
@Lyonk71
Copy link
Copy Markdown
Contributor Author

Lyonk71 commented Mar 14, 2026

/gemini review

Copy link
Copy Markdown
Contributor

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

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 a new sandbox implementation for Linux using bubblewrap (bwrap), providing a lightweight and performant alternative to Docker/Podman. The changes are well-structured, with clear separation between the outer CLI sandbox and the nested, more restrictive tool sandbox. My review focuses on ensuring consistency and correctness in the new implementation. I've identified a couple of areas for improvement: ensuring feature parity for environment variable configuration across all sandbox types, and improving the robustness of the Linux distribution detection logic.

Comment on lines +1258 to +1311
// Filter environment variables to prevent leakage of host credentials
const safeEnvKeys = [
'PATH',
'TERM',
'COLORTERM',
'LANG',
'LC_ALL',
'HOME',
'USER',
'LOGNAME',
'SHELL',
'PWD',
'EDITOR',
'VISUAL',
'DEBUG',
'DEBUG_PORT',
'NODE_ENV',
'NODE_OPTIONS',
'GEMINI_SANDBOX',
'GEMINI_SANDBOX_IMAGE',
'GEMINI_SANDBOX_IMAGE_DEFAULT',
'GEMINI_DIR',
'GEMINI_CLI_HOME',
'GEMINI_CLI_NO_RELAUNCH',
'SANDBOX_PORTS',
'SANDBOX_SET_UID_GID',
'SANDBOX_MOUNTS',
'SANDBOX_FLAGS',
'SEATBELT_PROFILE',
'HTTP_PROXY',
'HTTPS_PROXY',
'NO_PROXY',
'http_proxy',
'https_proxy',
'no_proxy',
'NO_BROWSER',
'GEMINI_API_KEY',
'GOOGLE_API_KEY',
'GOOGLE_APPLICATION_CREDENTIALS',
'GOOGLE_GEMINI_BASE_URL',
'GOOGLE_VERTEX_BASE_URL',
'GOOGLE_GENAI_USE_VERTEXAI',
'GOOGLE_GENAI_USE_GCA',
'GOOGLE_CLOUD_PROJECT',
'GOOGLE_CLOUD_LOCATION',
'GEMINI_MODEL',
];

const sandboxEnv: NodeJS.ProcessEnv = {};
for (const key of safeEnvKeys) {
if (process.env[key] !== undefined) {
sandboxEnv[key] = process.env[key];
}
}
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 bwrap sandbox implementation does not handle the SANDBOX_ENV environment variable, which is supported by the Docker/Podman sandbox implementations. This creates an inconsistency where users might expect to pass custom environment variables to any sandbox type using SANDBOX_ENV.

To ensure feature parity, please add support for SANDBOX_ENV to the bwrap sandbox. You can parse the variable and add the key-value pairs to the sandboxEnv object, which is then passed to the spawn call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gemini-code-assist - SANDBOX_ENV exists for Docker/Podman because containers start with a blank environment and need every variable explicitly passed in. bwrap runs on the host OS, so it takes the opposite approach — we inherit the host environment filtered through a safeEnvKeys allowlist to pass through only the minimum necessary set. Adding SANDBOX_ENV here would be redundant since the vars are already available on the host.

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.

Thanks for the clarification, Lyonk71. I understand that bwrap inherits the host environment and then filters it through safeEnvKeys, which is a different mechanism from Docker/Podman starting with a blank environment. However, this difference in mechanism actually highlights why SANDBOX_ENV is still necessary for feature parity.

The safeEnvKeys list acts as an allowlist. If a user wants to pass a custom environment variable (e.g., MY_API_KEY=12345) that is not already present in safeEnvKeys, it will not be propagated into the bwrap sandbox, even if it exists on the host. This limits the user's ability to inject specific, ad-hoc environment variables for their tools or scripts running within the sandbox.

SANDBOX_ENV in Docker/Podman serves precisely this purpose: to allow users to explicitly specify additional environment variables to be included in the sandbox, potentially overriding existing ones or adding new ones not in the default set. Without this, the bwrap sandbox lacks the flexibility that Docker/Podman sandboxes offer in terms of environment customization.

To achieve true feature parity and allow users to pass custom environment variables to the bwrap sandbox, I recommend adding support for parsing SANDBOX_ENV and incorporating those variables into the sandboxEnv object. This would ensure that users have a consistent way to configure their sandbox environment across different sandbox implementations.

I've updated the suggestion to include SANDBOX_ENV in the safeEnvKeys and to parse its contents, adding or overriding variables in sandboxEnv accordingly. This is a high priority issue as it impacts the functionality and flexibility of the bwrap sandbox for users who rely on custom environment variables.

Suggested change
// Filter environment variables to prevent leakage of host credentials
const safeEnvKeys = [
'PATH',
'TERM',
'COLORTERM',
'LANG',
'LC_ALL',
'HOME',
'USER',
'LOGNAME',
'SHELL',
'PWD',
'EDITOR',
'VISUAL',
'DEBUG',
'DEBUG_PORT',
'NODE_ENV',
'NODE_OPTIONS',
'GEMINI_SANDBOX',
'GEMINI_SANDBOX_IMAGE',
'GEMINI_SANDBOX_IMAGE_DEFAULT',
'GEMINI_DIR',
'GEMINI_CLI_HOME',
'GEMINI_CLI_NO_RELAUNCH',
'SANDBOX_PORTS',
'SANDBOX_SET_UID_GID',
'SANDBOX_MOUNTS',
'SANDBOX_FLAGS',
'SEATBELT_PROFILE',
'HTTP_PROXY',
'HTTPS_PROXY',
'NO_PROXY',
'http_proxy',
'https_proxy',
'no_proxy',
'NO_BROWSER',
'GEMINI_API_KEY',
'GOOGLE_API_KEY',
'GOOGLE_APPLICATION_CREDENTIALS',
'GOOGLE_GEMINI_BASE_URL',
'GOOGLE_VERTEX_BASE_URL',
'GOOGLE_GENAI_USE_VERTEXAI',
'GOOGLE_GENAI_USE_GCA',
'GOOGLE_CLOUD_PROJECT',
'GOOGLE_CLOUD_LOCATION',
'GEMINI_MODEL',
];
const sandboxEnv: NodeJS.ProcessEnv = {};
for (const key of safeEnvKeys) {
if (process.env[key] !== undefined) {
sandboxEnv[key] = process.env[key];
}
}
// Filter environment variables to prevent leakage of host credentials
const safeEnvKeys = [
'PATH',
'TERM',
'COLORTERM',
'LANG',
'LC_ALL',
'HOME',
'USER',
'LOGNAME',
'SHELL',
'PWD',
'EDITOR',
'VISUAL',
'DEBUG',
'DEBUG_PORT',
'NODE_ENV',
'NODE_OPTIONS',
'GEMINI_SANDBOX',
'GEMINI_SANDBOX_IMAGE',
'GEMINI_SANDBOX_IMAGE_DEFAULT',
'GEMINI_DIR',
'GEMINI_CLI_HOME',
'GEMINI_CLI_NO_RELAUNCH',
'SANDBOX_PORTS',
'SANDBOX_SET_UID_GID',
'SANDBOX_MOUNTS',
'SANDBOX_FLAGS',
'SEATBELT_PROFILE',
'HTTP_PROXY',
'HTTPS_PROXY',
'NO_PROXY',
'http_proxy',
'https_proxy',
'no_proxy',
'NO_BROWSER',
'GEMINI_API_KEY',
'GOOGLE_API_KEY',
'GOOGLE_APPLICATION_CREDENTIALS',
'GOOGLE_GEMINI_BASE_URL',
'GOOGLE_VERTEX_BASE_URL',
'GOOGLE_GENAI_USE_VERTEXAI',
'GOOGLE_GENAI_USE_GCA',
'GOOGLE_CLOUD_PROJECT',
'GOOGLE_CLOUD_LOCATION',
'GEMINI_MODEL',
'SANDBOX_ENV', // Add SANDBOX_ENV to the allowlist
];
const sandboxEnv: NodeJS.ProcessEnv = {};
for (const key of safeEnvKeys) {
if (process.env[key] !== undefined) {
sandboxEnv[key] = process.env[key];
}
}
// Process SANDBOX_ENV for additional environment variables
if (process.env['SANDBOX_ENV']) {
const customEnvVars = process.env['SANDBOX_ENV'].split(',');
for (const entry of customEnvVars) {
const parts = entry.split('=');
const key = parts[0].trim();
if (key) {
if (parts.length > 1) {
// Key=Value pair
sandboxEnv[key] = parts.slice(1).join('=').trim();
} else if (process.env[key] !== undefined) {
// Key only, copy from host environment
sandboxEnv[key] = process.env[key];
}
}
}
}
Suggested change
// Filter environment variables to prevent leakage of host credentials
const safeEnvKeys = [
'PATH',
'TERM',
'COLORTERM',
'LANG',
'LC_ALL',
'HOME',
'USER',
'LOGNAME',
'SHELL',
'PWD',
'EDITOR',
'VISUAL',
'DEBUG',
'DEBUG_PORT',
'NODE_ENV',
'NODE_OPTIONS',
'GEMINI_SANDBOX',
'GEMINI_SANDBOX_IMAGE',
'GEMINI_SANDBOX_IMAGE_DEFAULT',
'GEMINI_DIR',
'GEMINI_CLI_HOME',
'GEMINI_CLI_NO_RELAUNCH',
'SANDBOX_PORTS',
'SANDBOX_SET_UID_GID',
'SANDBOX_MOUNTS',
'SANDBOX_FLAGS',
'SEATBELT_PROFILE',
'HTTP_PROXY',
'HTTPS_PROXY',
'NO_PROXY',
'http_proxy',
'https_proxy',
'no_proxy',
'NO_BROWSER',
'GEMINI_API_KEY',
'GOOGLE_API_KEY',
'GOOGLE_APPLICATION_CREDENTIALS',
'GOOGLE_GEMINI_BASE_URL',
'GOOGLE_VERTEX_BASE_URL',
'GOOGLE_GENAI_USE_VERTEXAI',
'GOOGLE_GENAI_USE_GCA',
'GOOGLE_CLOUD_PROJECT',
'GOOGLE_CLOUD_LOCATION',
'GEMINI_MODEL',
];
const sandboxEnv: NodeJS.ProcessEnv = {};
for (const key of safeEnvKeys) {
if (process.env[key] !== undefined) {
sandboxEnv[key] = process.env[key];
}
}
// Filter environment variables to prevent leakage of host credentials
const safeEnvKeys = [
'PATH',
'TERM',
'COLORTERM',
'LANG',
'LC_ALL',
'HOME',
'USER',
'LOGNAME',
'SHELL',
'PWD',
'EDITOR',
'VISUAL',
'DEBUG',
'DEBUG_PORT',
'NODE_ENV',
'NODE_OPTIONS',
'GEMINI_SANDBOX',
'GEMINI_SANDBOX_IMAGE',
'GEMINI_SANDBOX_IMAGE_DEFAULT',
'GEMINI_DIR',
'GEMINI_CLI_HOME',
'GEMINI_CLI_NO_RELAUNCH',
'SANDBOX_PORTS',
'SANDBOX_SET_UID_GID',
'SANDBOX_MOUNTS',
'SANDBOX_FLAGS',
'SEATBELT_PROFILE',
'HTTP_PROXY',
'HTTPS_PROXY',
'NO_PROXY',
'http_proxy',
'https_proxy',
'no_proxy',
'NO_BROWSER',
'GEMINI_API_KEY',
'GOOGLE_API_KEY',
'GOOGLE_APPLICATION_CREDENTIALS',
'GOOGLE_GEMINI_BASE_URL',
'GOOGLE_VERTEX_BASE_URL',
'GOOGLE_GENAI_USE_VERTEXAI',
'GOOGLE_GENAI_USE_GCA',
'GOOGLE_CLOUD_PROJECT',
'GOOGLE_CLOUD_LOCATION',
'GEMINI_MODEL',
'SANDBOX_ENV', // Add SANDBOX_ENV to the allowlist
];
const sandboxEnv: NodeJS.ProcessEnv = {};
for (const key of safeEnvKeys) {
if (process.env[key] !== undefined) {
sandboxEnv[key] = process.env[key];
}
}
// Process SANDBOX_ENV for additional environment variables
if (process.env['SANDBOX_ENV']) {
const customEnvVars = process.env['SANDBOX_ENV'].split(',');
for (const entry of customEnvVars) {
const parts = entry.split('=');
const key = parts[0].trim();
if (key) {
if (parts.length > 1) {
// Key=Value pair
sandboxEnv[key] = parts.slice(1).join('=').trim();
} else if (process.env[key] !== undefined) {
// Key only, copy from host environment
sandboxEnv[key] = process.env[key];
}
}
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gemini-code-assist - You're being verbose, and repeating yourself, but I think you are missing the point. For any system specific envars, we're already covered with safeEnvKeys. SANDBOX_ENV isn't relevant because our sandboxed tools don't have internet access like podman does. The reason is so that a malicious prompt injection can't pawn our local machine. It's a tradeoff we made to have a more secure sandbox at the expense of some flexibility. It's hard to consider a SANDBOX_ENV value that is not:

  1. A system envar (already covered)
  2. Used for remote systems (locked down)

We'd be adding complexity for parity theater that doesn't improve the product.

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.

Thanks for elaborating on the security considerations, Lyonk71. I understand the design choice to prioritize security by disabling network access for tools within the bwrap sandbox using --unshare-net, and that this is a deliberate trade-off.

My point regarding SANDBOX_ENV is not about network access or remote systems, but rather about environment customization and feature parity for local variables. While safeEnvKeys effectively filters existing host environment variables, it doesn't provide a mechanism for users to inject new, custom environment variables that are not already on that allowlist.

Consider a scenario where a user wants to pass a specific configuration flag, like MY_TOOL_CONFIG=debug_mode, to a tool running inside the bwrap sandbox. If MY_TOOL_CONFIG is not in safeEnvKeys, it will not be passed, even if it's a purely local variable with no security implications related to network access. Docker/Podman sandboxes allow this kind of custom variable injection via SANDBOX_ENV.

Adding support for SANDBOX_ENV to bwrap would allow users to define such custom, local environment variables, enhancing the flexibility and configurability of the sandbox without compromising the existing network isolation. The bwrap --unshare-net flag would still prevent network access for tools, regardless of what is passed via SANDBOX_ENV.

This change aims to provide a consistent user experience across different sandbox implementations for environment variable management, which is a key aspect of feature parity, and does not introduce new network vulnerabilities. The suggested code ensures that SANDBOX_ENV is processed and its contents are added to the sandboxEnv object, respecting the existing security model.

…spec

Replace regex substring matching with proper parsing of the ID and
ID_LIKE fields from /etc/os-release. This correctly handles multi-value
ID_LIKE entries like "rhel fedora" without false positives on partial
substring matches.
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli bot commented Mar 28, 2026

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 have updated our contribution policy (see Discussion #17383).

We only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'. All other community pull requests are subject to closure after 14 days if they do not align with our current focus areas. For this reason, we strongly recommend that contributors only submit pull requests against issues explicitly labeled as 'help-wanted'.

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 and for being part of our community!

@gemini-cli gemini-cli bot closed this Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant