Skip to content

feat(core): refactor SandboxManager to a stateless architecture and introduce explicit Deny interface#23141

Open
ehedlund wants to merge 1 commit intomainfrom
feature/sandbox-request
Open

feat(core): refactor SandboxManager to a stateless architecture and introduce explicit Deny interface#23141
ehedlund wants to merge 1 commit intomainfrom
feature/sandbox-request

Conversation

@ehedlund
Copy link
Contributor

@ehedlund ehedlund commented Mar 19, 2026

Summary

This PR refactors the SandboxManager and its OS-specific implementations into a stateless architecture and introduces the infrastructure for explicit "Deny" rules (forbiddenPaths). By standardizing the configuration flow across macOS, Linux, and Windows, we've created a more robust foundation for dynamic security policies.

Details

1. Stateless Architecture

  • Refactored OS Drivers: MacOsSandboxManager, LinuxSandboxManager, and windowsSandboxManager no longer hold per-request state. They are initialized with a static GlobalSandboxOptions (containing the workspace root) and receive a dynamic ExecutionPolicy for every command.
  • Explicit Lifecycle Scoping: Introduced GlobalSandboxOptions (startup-time) and ExecutionPolicy (execution-time) interfaces to clarify when specific security boundaries are applied.

2. Standardized Path Processing

  • Cross-Platform Sanitization: Implemented a central sanitizePaths utility that deduplicates paths and enforces absolute-path requirements. It correctly handles case-insensitivity on Windows while preserving original casing.
  • Improved Resilience: Updated the Linux driver to use --bind-try for dynamic paths, preventing sandbox initialization crashes if an allowed path is missing on the host.

3. OS Integration & Security

  • Logic Consolidation: Consolidated the macOS Seatbelt logic into MacOsSandboxManager, removing the separate builder file.
  • Windows Security Alignment: Updated the Windows driver to use the centralized getSecureSanitizationConfig helper, ensuring it inherits the same strict secret-filtering and redaction policies as other platforms.
  • Explicit Deny Interface: Added the forbiddenPaths field to the execution policy across all platforms (enforcement logic remains a TODO for a subsequent PR).

4. Test Suite Refactoring

  • Consolidated path validation and deduplication tests into sandboxManager.test.ts.
  • Refactored OS-specific tests to eliminate massive duplication and focus on low-level argument translation.

Related Issues

Resolves https://github.com/google-gemini/maintainers-gemini-cli/issues/1556

How to Validate

  1. Verify all core unit and integration tests pass:
    npm test -w @google/gemini-cli-core
  2. Verify the centralized path sanitization logic:
    npm test -w @google/gemini-cli-core -- src/services/sandboxManager.test.ts

Pre-Merge Checklist

  • Added/updated tests (if needed)
  • Validated on required platforms/methods:
    • MacOS (Seatbelt)
    • Linux (Bubblewrap)
    • Windows (Low Integrity)

@ehedlund ehedlund requested a review from a team as a code owner March 19, 2026 19:15
@gemini-cli
Copy link
Contributor

gemini-cli bot commented Mar 19, 2026

Hi @ehedlund, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.

We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: Improving Our Contribution Process & Introducing New Guidelines.

Key Update: Starting January 26, 2026, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.

Thank you for your understanding and for being a part of our community!

@gemini-code-assist
Copy link
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 significantly enhances the security and maintainability of the sandboxing mechanism by transitioning the SandboxManager to a stateless design. This architectural shift simplifies manager lifecycles and ensures that security policies are explicitly defined for each execution request. It also lays the groundwork for more granular control by introducing path-level deny rules, allowing for more precise security boundaries.

Highlights

  • Stateless SandboxManager Architecture: Refactored SandboxManager to a stateless architecture, moving per-request configuration to an ExecutionPolicy.
  • Path-level Deny Rules: Introduced explicit 'Deny' rules (forbiddenPaths) within the ExecutionPolicy for enhanced security.
  • macOS Logic Consolidation: Consolidated macOS sandbox logic and related helper methods directly into MacOsSandboxManager.
  • Test Refactoring: Refactored and consolidated sandbox tests across the core package to align with the new stateless design.
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.

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.

@ehedlund ehedlund force-pushed the feature/sandbox-request branch from 1258ef4 to 910bb9c Compare March 19, 2026 19:17
@github-actions
Copy link

github-actions bot commented Mar 19, 2026

Size Change: +1.19 kB (0%)

Total Size: 26.1 MB

Filename Size Change
./bundle/chunk-3VXP3BVD.js 0 B -3.64 MB (removed) 🏆
./bundle/chunk-YIK5TYK7.js 0 B -13.6 MB (removed) 🏆
./bundle/core-WLKHGF33.js 0 B -42.2 kB (removed) 🏆
./bundle/devtoolsService-TFRXJXO3.js 0 B -27.7 kB (removed) 🏆
./bundle/interactiveCli-PU54OKDI.js 0 B -1.61 MB (removed) 🏆
./bundle/oauth2-provider-ZPQ5RNOT.js 0 B -9.19 kB (removed) 🏆
./bundle/chunk-4LJSVPIB.js 3.64 MB +3.64 MB (new file) 🆕
./bundle/chunk-PELSFJRB.js 13.6 MB +13.6 MB (new file) 🆕
./bundle/core-G65MYXSF.js 42.2 kB +42.2 kB (new file) 🆕
./bundle/devtoolsService-3UYFMOSL.js 27.7 kB +27.7 kB (new file) 🆕
./bundle/interactiveCli-E7DAH5IQ.js 1.61 MB +1.61 MB (new file) 🆕
./bundle/oauth2-provider-KKYOF5IN.js 9.19 kB +9.19 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size
./bundle/chunk-34MYV7JD.js 2.45 kB
./bundle/chunk-37ZTTFQF.js 966 kB
./bundle/chunk-3O3NCGL6.js 1.95 MB
./bundle/chunk-5AUYMPVF.js 858 B
./bundle/chunk-664ZODQF.js 124 kB
./bundle/chunk-DAHVX5MI.js 206 kB
./bundle/chunk-IUUIT4SU.js 56.5 kB
./bundle/chunk-RJTRUG2J.js 39.8 kB
./bundle/devtools-36NN55EP.js 696 kB
./bundle/dist-T73EYRDX.js 356 B
./bundle/gemini.js 519 kB
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB
./bundle/memoryDiscovery-UDJRMDUJ.js 922 B
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 221 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 227 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 11.5 kB
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B
./bundle/sandbox-macos-permissive-open.sb 890 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB
./bundle/sandbox-macos-strict-open.sb 4.82 kB
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB
./bundle/src-QVCVGIUX.js 47 kB
./bundle/tree-sitter-7U6MW5PS.js 274 kB
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB
./bundle/undici-4X2YZID5.js 360 B

compressed-size-action

Copy link
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 refactors sandbox policy management by introducing a new ExecutionPolicy interface and moving policy-specific configurations (like allowedPaths, networkAccess, and sanitizationConfig) from global manager options or a generic config object directly into the SandboxRequest's policy field. This change affects LinuxSandboxManager and MacOsSandboxManager, making sandbox policies dynamic per execution request rather than static per manager instance. The MacOsSandboxManager also internalizes the logic for building Seatbelt arguments, removing a separate seatbeltArgsBuilder module, and includes robust path normalization with symlink resolution. Additionally, a new forbiddenResourceService is introduced to identify resources that should be excluded from the sandbox based on .gitignore and .geminiignore files, complete with new unit tests. However, the current implementation has two high-severity security vulnerabilities: the MacOsSandboxManager ignores the forbiddenPaths policy, potentially allowing access to restricted subpaths within allowed directories, and the ShellExecutionService fails to pass forbiddenPaths to the sandbox manager, preventing their enforcement.

@gemini-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Mar 19, 2026
@ehedlund ehedlund enabled auto-merge March 19, 2026 19:48
Copy link
Collaborator

@galz10 galz10 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

Scope: Pull Request #23141

This PR successfully refactors the SandboxManager implementations (MacOsSandboxManager and LinuxSandboxManager) to a stateless architecture. By migrating configurations like allowedPaths and networkAccess from the constructor to a per-request ExecutionPolicy, the lifecycle of the managers is simplified and the foundation is laid for dynamic security boundaries. The consolidation of macOS Seatbelt logic into the manager class itself is also a nice cleanup.

There are a few areas that need minor adjustments before merging, primarily regarding the missing OS-specific implementations for the newly introduced forbiddenPaths interface and some edge cases in the path handling.

Metadata Review

The PR title and description clearly explain the motivation and the technical changes made. The Conventional Commits prefix (feat(core):) is used correctly. However, the title and body claim to have introduced "support for path-level Deny rules", but the underlying OS manager implementations for forbiddenPaths are currently missing.

Concerns (Action Required)

  • packages/core/src/sandbox/macos/MacOsSandboxManager.ts & packages/core/src/sandbox/linux/LinuxSandboxManager.ts:
    Missing OS-level implementation for forbiddenPaths. The PR explicitly mentions adding support for explicit "Deny" rules (forbiddenPaths). While forbiddenPaths has been added to the ExecutionPolicy interface, neither the macOS nor the Linux sandbox managers process this array.

    Since the interface is in place, if the OS-specific implementation is out of scope for this current PR:

    1. Please add TODO comments in both managers where forbiddenPaths will be processed.
    2. Please create child bugs/issues for adding the OS-specific implementations (e.g., adding (deny file-read* file-write* (subpath (param "FORBIDDEN_PATH_X"))) to the Seatbelt profile for macOS, and masking directories/files for Linux bwrap) and link them in the PR description or code comments.
    3. You may also want to update the PR title/description slightly to clarify that only the interface/infrastructure for forbiddenPaths was added in this step.

Nits (Minor Suggestions / Enhancements)

  • packages/core/src/sandbox/linux/LinuxSandboxManager.ts:
    Use --bind-try for dynamically allowed paths. Currently, the code uses --bind for allowedPaths. If a path in allowedPaths does not exist on the host filesystem, bwrap will immediately fail to start. Using --bind-try tells Bubblewrap to silently ignore the bind if the source path doesn't exist. This aligns its behavior more closely with macOS Seatbelt (which handles non-existent paths gracefully) and prevents execution crashes for dynamically evaluated paths.

  • packages/core/src/sandbox/linux/LinuxSandboxManager.ts:
    Robust Workspace Path Comparison. The check if (path !== this.options.workspace) is a strict string comparison. If options.workspace is /workspace but an allowed path is passed as /workspace/ (with a trailing slash), the condition will evaluate to true and it will attempt to bind the workspace twice. Consider using path.normalize() or path.resolve() on both strings before comparing them to ensure accurate matching.

  • packages/core/src/sandbox/macos/MacOsSandboxManager.ts & packages/core/src/sandbox/linux/LinuxSandboxManager.ts:
    Deduplicate allowedPaths. Currently, if req.policy?.allowedPaths contains duplicate entries, both managers will process them redundantly (macOS will generate duplicate Seatbelt parameters/rules, and Linux will push duplicate --bind arguments). Consider deduplicating the paths (e.g., Array.from(new Set(req.policy?.allowedPaths || []))) before processing them.

  • packages/core/src/services/sandboxManager.ts:
    ExecutionPolicy Documentation. The comment for forbiddenPaths mentions "Absolute paths". However, the current macOS implementation (tryRealpath) natively converts relative paths to absolute ones via fs.realpathSync. It might be worth documenting whether allowedPaths and forbiddenPaths are strictly required to be absolute, or if the managers will handle resolving them if relative paths are passed.

  • packages/core/src/sandbox/macos/MacOsSandboxManager.ts:
    Profile String Construction. The Seatbelt profile string is built via string concatenation (profile += ...). While functionally correct and safe here since you are properly using parameterized bindings (-D ALLOWED_PATH_X=...), using a string array and join('\n') might be slightly cleaner for readability if this profile generation logic grows more complex in the future.

@ehedlund ehedlund force-pushed the feature/sandbox-request branch from 29b4d45 to be84ca7 Compare March 20, 2026 15:20
@ehedlund ehedlund requested review from a team as code owners March 20, 2026 15:20
@ehedlund ehedlund force-pushed the feature/sandbox-request branch from be84ca7 to 936abb0 Compare March 20, 2026 15:23
@ehedlund ehedlund removed request for a team March 20, 2026 15:24
@ehedlund ehedlund force-pushed the feature/sandbox-request branch from 936abb0 to 930206c Compare March 20, 2026 15:42
@ehedlund ehedlund changed the title feat(core): refactor SandboxManager to a stateless architecture with support for path-level Deny rules feat(core): refactor SandboxManager to a stateless architecture and introduce explicit Deny interface Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

2 participants