Repository navigation
sys.unixShellCommand can exec a host GUI browser past the #891 headless openUrl guard -- decision needed #894
Description
Rationale
#891 stopped the headless build from launching a host GUI browser: sys.openUrl now logs the URL and records it at system.temp.Frontier.pendingBrowserUrl instead of forking a launcher, unless an explicit --browser allow-gui opt-in is given.
That guard is placed at the sys.openUrl kernel verb, which is the single choke point for every webBrowser.openUrl caller. It is not a general guarantee that a headless process will not reach the host desktop, because sys.unixShellCommand is in the CLI build and can do the same thing directly:
sys.unixShellCommand("open http://127.0.0.1:5336/setupFrontier")
This was identified during #891 and deliberately left unguarded there — guarding it is a separate decision with its own trade-offs, and #891 was scoped to the onboarding path.
Current State
Common/source/sysshellcall.c:232/:324—popeninto/bin/sh; this file IS in the CLI buildtests/headless_sys_verbs.c:812—case sysv_unixshellcommand, the headless twin (see the duplication issue for the link-time ambiguity between these two)- No headless-awareness in either path: a shell command runs whatever it is given
Scope note: launch.* verbs are already no-ops in headless (tests/headless_launch_verbs.c:41-54), so they are not a third door.
The actual decision
sys.unixShellCommand is a general-purpose shell escape. Unlike sys.openUrl — which has exactly one purpose, so restricting it costs nothing — a shell verb has no narrow contract to enforce. Any guard is a policy choice about what a headless Frontier process may do to its host, and needs to be made deliberately:
- Leave it. Scripts in the ODB are trusted code; a shell verb is expected to be able to run shell commands. The Headless webBrowser.openUrl should log setup URL for remote operators, not auto-launch a host GUI browser (preserve first-run onboarding intent) #891 guard then covers the onboarding path only, and that is stated honestly rather than implied to be airtight.
- Pattern-guard the launchers. Reject or neutralize
open/xdg-openinvocations in headless mode. Cheap, but trivially bypassed (/usr/bin/open,sh -c, a variable) — real risk of a guard that reads as protection while providing little. - Sandbox or policy-gate the verb. An explicit opt-in for shell execution in headless/server contexts, in the same shape as
--browser allow-gui. Most defensible, largest surface, and would need its own design pass.
Option 2 is the tempting one and probably the worst: it adds the appearance of enforcement without the substance.
Desired Outcome
A recorded decision on which of the above applies, and — whichever is chosen — documentation that accurately states the boundary of the #891 guard, so nobody reads "headless will not launch a browser" as stronger than it is.
Exit Criteria
- Decision recorded on guarding vs. not guarding
sys.unixShellCommandin headless contexts - If guarded: behavioral test proving the blocked path, using a stub launcher (never a real GUI browser)
- Documentation states what the Headless webBrowser.openUrl should log setup URL for remote operators, not auto-launch a host GUI browser (preserve first-run onboarding intent) #891 guard does and does not cover
- No regression to the Headless webBrowser.openUrl should log setup URL for remote operators, not auto-launch a host GUI browser (preserve first-run onboarding intent) #891 headless-default behavior
Related Context
- Found by: Headless webBrowser.openUrl should log setup URL for remote operators, not auto-launch a host GUI browser (preserve first-run onboarding intent) #891 (headless
sys.openUrlGUI-browser guard), 2026-08-15 - Code:
Common/source/sysshellcall.c,tests/headless_sys_verbs.c - Testing constraint carried over from Headless webBrowser.openUrl should log setup URL for remote operators, not auto-launch a host GUI browser (preserve first-run onboarding intent) #891: no test may launch a host GUI browser; exec assertions use PATH-shadowing stub launchers
Activity