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):
Next (separate PRs):
Client-side copy: oablab/oab-pty-mac (linked issue).
Summary
The
sandboxtool 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 calldo shell script, and so run anything the desktop user can run. Lending a computer undersandboxtherefore hands the agent the desktop user's shell for the whole lease (up to 24 h).External review, 2026-09-30, confirmed against
main:ToolProfile.sandboxexec*, becauseexecis a full shell as the desktop user"exec*is removed;osascript(do shell script),key(type into Terminal) andmouseremainsandboxowner, includingbash(mcp.rs: "Both profiles get everything")sandboxBrowserToolsbrowser_evaluate--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.
osascriptin particular cannot be made safe by filtering script text: JXAdoShellScript,ObjC.import→NSTask,run script, Terminaldo script, System Events keystrokes all bypass it.Plan
Now (this PR):
browser_evaluatefromsandboxBrowserTools(Swift + Linux). This is a bug: it contradicts the README.desktop. Keep acceptingsandboxas 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.exec*,osascript,key,mouse,bash). Today this forcesdesktopto be declared shell-equivalent; later it guards any new restricted profile.desktop, and stop describingsandboxas narrower.Next (separate PRs):
observeprofile (sys_info+screenshotonly) as a real boundary, and the suggested default in the grant UIs.browserprofile: Playwright only, with a per-grant throwaway browser profile, nobrowser_evaluate, and the host-network reach documented.osascriptfor 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.osascript, or for keys sent while the front app is a terminal. Document these as friction, not a boundary.Client-side copy: oablab/oab-pty-mac (linked issue).