Skip to content

fix(cli): fall back to embedded macOS seatbelt profiles if missing - #28551

Open
amelidev wants to merge 10 commits into
google-gemini:mainfrom
amelidev:b_484367016
Open

fix(cli): fall back to embedded macOS seatbelt profiles if missing#28551
amelidev wants to merge 10 commits into
google-gemini:mainfrom
amelidev:b_484367016

Conversation

@amelidev

@amelidev amelidev commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR resolves the critical startup crash when running Gemini CLI in sandbox mode (-s) on macOS/gMac environments where static Seatbelt .sb profiles are not found in the runfiles or bundle folder.

Details

  • The Problem: Non-JS static assets (like .sb seatbelt profiles) are often stripped or omitted from the deployed bundle (e.g. bundle/gemini.js or gemini.mjs in Bazel/google3) because they are not traced by standard JS bundlers/packagers.
  • The Solution: We embed the contents of the six built-in macOS seatbelt profiles inside sandboxBuiltinProfiles.ts.
  • The Fallback: If fs.existsSync(profileFile) returns false at runtime, we fall back to writing the embedded profile string to a unique, randomized temporary file under os.tmpdir() (gemini-sandbox-macos-${profile}-${rand}.sb).
  • Dynamic Cleanup: We register cleanup listeners strictly on the 'exit' event (to avoid signal hijacking / process lockup of SIGINT/SIGTERM handlers) and on spawned child process termination (close, error) to cleanly unlink the temporary file.
  • Security Hardening (NODE_OPTIONS Quoting): Escaped/quoted the NODE_OPTIONS argument string via the quote utility inside the sh -c invocation to neutralize potential shell command injection vulnerabilities.
  • Backward Compatibility: Original local development behavior remains completely unchanged, as the fallback ONLY triggers if the physical .sb file is missing.

Related Issues

Resolves the issue reported in issue.md.

How to Validate

We have added robust unit test coverage to verify this fallback behavior. Run the targeted tests for the touched module:

npm test -w @google/gemini-cli -- src/utils/sandbox.test.ts

All 22 tests (including our new fallback test) compile, pass, and satisfy all linting rules cleanly!

Pre-Merge Checklist

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

@amelidev
amelidev requested a review from a team as a code owner July 27, 2026 22:04
@github-actions github-actions Bot added the size/l A large sized PR label Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

📊 PR Size: size/L

  • Lines changed: 992
  • Additions: +843
  • Deletions: -149
  • Files changed: 6

@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 resolves a critical startup crash in the Gemini CLI's sandbox mode on macOS, which occurred when static Seatbelt profiles were missing from the bundle. By embedding these profiles directly into the source code, the CLI can now dynamically generate the necessary sandbox configuration at runtime, ensuring consistent behavior across different deployment environments.

Highlights

  • Embedded Seatbelt Profiles: Added a new utility file containing the six built-in macOS seatbelt profiles to ensure they are available even when static files are missing from the deployment bundle.
  • Runtime Fallback Mechanism: Implemented a fallback in the sandbox initialization logic that writes embedded profiles to a temporary file in the system's temporary directory if the expected .sb file is not found on disk.
  • Automatic Cleanup: Added robust cleanup logic to ensure temporary profile files are unlinked upon process exit, termination signals, or child process completion.
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 implements a fallback mechanism for the macOS seatbelt sandbox, writing embedded profile contents to a temporary file when the profile is missing on disk, and adds corresponding unit tests. Feedback on these changes highlights a potential command injection vulnerability when constructing the shell command with unescaped NODE_OPTIONS. Additionally, the feedback advises against registering custom SIGINT and SIGTERM handlers for temporary file cleanup, as they can hijack termination signals and prevent the CLI process from exiting properly, suggesting to rely solely on the exit event instead.

Comment thread packages/cli/src/utils/sandbox.ts Outdated
Comment thread packages/cli/src/utils/sandbox.ts Outdated
Comment thread packages/cli/src/utils/sandbox.ts Outdated
@amelidev

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 implements a fallback mechanism in the macOS sandbox to use embedded seatbelt profiles when the .sb files are not present on disk. It introduces a new file, sandboxBuiltinProfiles.ts, containing the predefined seatbelt profiles, and updates sandbox.ts to write these profiles to a temporary file and handle their cleanup. Additionally, a new unit test has been added to verify this fallback behavior. There are no review comments to address, and the changes look solid.

@DavidAPierce

Copy link
Copy Markdown
Contributor

Overall LGTM, but I would like to see two minor enhancements before approval and merging.
Below are minor recommendations to enhance security and signal handling:

💡 Recommendation 1: Restrict File Permissions on Temp Profile File

When writing temporary sandbox profiles to shared OS temp directories (/tmp or os.tmpdir()), explicitly restrict file access mode to 0o600 (read/write by owner only) to prevent other local users on shared systems from inspecting or tampering with temporary sandbox policy files:

fs.writeFileSync(tempProfileFile, content, {
  encoding: 'utf8',
  mode: 0o600,
});

💡 Recommendation 2: Handle SIGINT / SIGTERM Signal Cleanup

Ensure cleanupTempProfile is also bound to SIGINT and SIGTERM signals so temp files are cleaned up if a user terminates the CLI with Ctrl+C while the sandbox is initializing:

process.on('exit', cleanupTempProfile);
process.on('SIGINT', cleanupTempProfile);
process.on('SIGTERM', cleanupTempProfile);

@luisfelipe-alt

Copy link
Copy Markdown
Contributor

/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 support for falling back to embedded macOS seatbelt profiles when the physical .sb files are missing on disk (e.g., in bundled or bazel environments). It writes the embedded profile contents to a temporary file, registers cleanup handlers, and adds corresponding unit tests. Additionally, it configures the 'en' locale for yargs in several test files. The review feedback highlights a critical issue where registering cleanup handlers directly to SIGINT and SIGTERM signals hijacks them, preventing clean process termination. The reviewer suggests implementing dedicated signal handlers that perform the cleanup and then re-kill the process with the respective signal to allow standard termination.

Comment thread packages/cli/src/utils/sandbox.ts Outdated
Comment thread packages/cli/src/utils/sandbox.ts Outdated
Comment thread packages/cli/src/utils/sandbox.ts
@luisfelipe-alt

Copy link
Copy Markdown
Contributor

/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 embedded macOS seatbelt profiles to allow the sandbox to function when profile files are missing from disk, writing them to temporary files and managing their cleanup. It also updates CLI tests to use the English locale for consistent yargs validation. The review feedback highlights critical improvements for sandbox execution: resolving a POSIX shell compatibility issue where the entire NODE_OPTIONS assignment is incorrectly quoted, and unifying the signal handling and cleanup logic to prevent cascading signal delivery and redundant proxy termination code.

Comment thread packages/cli/src/utils/sandbox.ts Outdated
Comment thread packages/cli/src/utils/sandbox.ts Outdated
Comment thread packages/cli/src/utils/sandbox.ts Outdated
@amelidev

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 a fallback mechanism for macOS sandbox execution when seatbelt profile files (.sb) are missing from the disk (such as in bundled or Bazel environments). It adds a new file sandboxBuiltinProfiles.ts containing the embedded seatbelt profile contents and updates sandbox.ts to write these contents to a temporary file when necessary. It also implements robust cleanup logic for these temporary files and proxies using process signal handlers (SIGINT, SIGTERM, exit). Additionally, unit tests are updated to cover this fallback behavior, and several CLI command tests are updated to explicitly set the yargs locale to English. I have no feedback to provide as there are no review comments.

@DavidAPierce

Copy link
Copy Markdown
Contributor

Overall, the architecture for embedded fallback profiles, file mode restriction (0o600), and signal handling is well-designed. However, there is 1 critical blocking bug in the embedded profile dictionary keys that must be fixed before merging.


🔴 Blocking Issue

1. Mismatch between BUILTIN_SEATBELT_PROFILES keys and BUILTIN_SEATBELT_PROFILE_CONTENTS

  • Location: packages/cli/src/utils/sandboxBuiltinProfiles.ts

  • Problem:
    BUILTIN_SEATBELT_PROFILES in sandboxUtils.ts defines the standard profile names as:

    'permissive-open',
    'permissive-closed',
    'permissive-proxied',
    'restrictive-open',
    'restrictive-closed',
    'restrictive-proxied'

    However, in sandboxBuiltinProfiles.ts, the dictionary keys exported are:

    • 'permissive-open'
    • 'permissive-proxied'
    • 'restrictive-open'
    • 'restrictive-proxied'
    • 'strict-open' (Mismatch)
    • 'strict-proxied' (Mismatch)
  • Impact:
    If a user runs the sandbox with permissive-closed or restrictive-closed when .sb files are missing from disk, BUILTIN_SEATBELT_PROFILE_CONTENTS[profile] evaluates to undefined. As a result, the embedded fallback is skipped and the command throws FatalSandboxError: Missing macos seatbelt profile file.

  • Suggested Fix:
    Update the object keys in sandboxBuiltinProfiles.ts:

    export const BUILTIN_SEATBELT_PROFILE_CONTENTS: Record<string, string> = {
      'permissive-open': `...`,
    + 'permissive-closed': `...`,
      'permissive-proxied': `...`,
      'restrictive-open': `...`,
    + 'restrictive-closed': `...`,
      'restrictive-proxied': `...`,
    - 'strict-open': `...`,
    - 'strict-proxied': `...`,
    };

💡 Minor Recommendations

  • Double-check Test Coverage for *-closed Profiles:
    Consider adding unit tests in packages/cli/src/utils/sandbox.test.ts for all 6 builtin profiles to guarantee fallback mapping for every profile variant.

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.

3 participants