Skip to content

sandbox profile is not a boundary: GUI control is a shell #45

Description

@chaodu-agent

Summary

The sandbox tool profile is presented as a restricted mode, but it is not a security boundary. GUI control is shell access. An agent that can type, click or run AppleScript can open a terminal, or call do shell script, and so run anything the desktop user can run. Lending a computer under sandbox therefore hands the agent the desktop user's shell for the whole lease (up to 24 h).

External review, 2026-09-30, confirmed against main:

Claimed Actual
macOS ToolProfile.sandbox "no exec*, because exec is a full shell as the desktop user" only exec* is removed; osascript (do shell script), key (type into Terminal) and mouse remain
Linux node sandbox profile names are shared with macOS identical to owner, including bash (mcp.rs: "Both profiles get everything")
sandboxBrowserTools README: "not arbitrary code in the browser process" the list includes browser_evaluate
Playwright upstream — persistent shared profile (--user-data-dir pw-profile --shared-browser-context): the agent browses with the user's logged-in cookies, and from the host's network (localhost + tailnet)

A name-filtered tool list inside one desktop session cannot draw a boundary. osascript in particular cannot be made safe by filtering script text: JXA doShellScript, ObjC.import → NSTask, run script, Terminal do script, System Events keystrokes all bypass it.

Plan

Now (this PR):

  • Drop browser_evaluate from sandboxBrowserTools (Swift + Linux). This is a bug: it contradicts the README.
  • Rename the profile to desktop. Keep accepting sandbox as a wire / persisted alias, so existing Connect / Remote builds and stored grants keep working. The doc comments must say plainly that it is shell-equivalent.
  • Adversary test: every profile declares whether it is shell-equivalent, and a test fails if a profile that says "no shell" exposes any shell-capable tool (exec*, osascript, key, mouse, bash). Today this forces desktop to be declared shell-equivalent; later it guards any new restricted profile.
  • README + reverse-attach ADR: replace "the agent already has a shell, so no exec" with the honest statement, and recommend lending a dedicated machine (e.g. a Linux hands node) rather than a primary Mac when full control is granted.
  • Linux node: accept desktop, and stop describing sandbox as narrower.

Next (separate PRs):

  • observe profile (sys_info + screenshot only) as a real boundary, and the suggested default in the grant UIs.
  • browser profile: Playwright only, with a per-grant throwaway browser profile, no browser_evaluate, and the host-network reach documented.
  • Replace generic osascript for restricted tiers with typed tools (open app / click menu item / list windows) and a bundle-ID allowlist that excludes Terminal, iTerm, Script Editor and System Settings.
  • Visibility: menu-bar indicator of the active grant + live tool-call log + one-click revoke. Optional Remote approval for osascript, or for keys sent while the front app is a terminal. Document these as friction, not a boundary.
  • Long term: lend a disposable macOS VM (Virtualization.framework / Tart) instead of the host.

Client-side copy: oablab/oab-pty-mac (linked issue).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions