Repository navigation
fix(tsx): refuse packages with capabilities on the web, where the page cannot isolate them - #195
Merged
Merged
Conversation
houko
force-pushed
the
fix/web-sandbox-fail-closed
branch
from
November 15, 2026 06:23
cb1e5d4 to
73f5b22
Compare
houko
force-pushed
the
fix/web-sandbox-fail-closed
branch
from
November 15, 2026 12:59
73f5b22 to
5cc54fc
Compare
…e cannot isolate them On the web every js.Runtime is a hidden iframe of the page's origin, so a sandboxed package's realm reaches the page's XMLHttpRequest, WebSocket, navigator, localStorage and window past Go. Its capabilities held only the requests the runtime and core make, and its storage partition and a Trust kept in the page's storage were open to it, while the docs promised enforcement on every target. Sandbox.start, and so Sandboxed, View and Load, now refuses on the web a sandbox with Capabilities or a Grant with ErrUnisolated, unless the host sets Sandbox.WebUnisolated to run it with the page's authority. A sandbox with neither still runs: it reaches what the app's code does on every target. Load returns the refusal rather than sending it to a Catch. examples/plugins shows the refusal on the web. docs/architecture/tsx.md, the public TSX guide and the roadmap record the exception and the isolation still to come.
…storage or host on the web
houko
force-pushed
the
fix/web-sandbox-fail-closed
branch
from
November 15, 2026 13:44
5cc54fc to
1584ae1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
On the web every
js.Runtimeis a hidden iframe of the page's origin, and a sandboxed package runs in such a realm. Nothing removes the realm's own browser globals, so package code reaches the page'sXMLHttpRequest,WebSocket,navigator(clipboard included),localStorageandparentwithout going through Go. Capabilities andSandbox.Granttherefore held only the requests the runtime and core make; a package's storage partition and apkg.Trustkept in the page'slocalStorage(accepted builds, revocations) were open to it. The docs promised capability enforcement on every target with no web exception.This makes the web fail closed:
Sandbox.start(and sotsx.Sandboxed,tsx.Viewandtsx.Load) refuses on the web a sandbox with non-nilCapabilitiesorGrantwith the newtsx.ErrUnisolated, whose message names the missing isolation, unless the host sets the newSandbox.WebUnisolated, which states the package runs with the page's authority.ViewandLoadalways setCapabilities(the package's own), so every package is refused on the web by default. Other targets ignore the field.CapabilitiesnorGrantstill runs on the web: its contract is already "reaches what the app's code does" on every target, which is exactly what the page's realm gives it. This keepsnsgo dev -limitswithout capabilities andtsx.Sandboxedused for limits alone working. On the web such a sandbox'sStoragepartition andHostdo not confine it either, whichdocs/architecture/tsx.mdnow says.nsgo dev -limits -target webfor an app whosensgo.yamldeclarescapabilities:now shows theErrUnisolatedrefusal, as a host'sViewof the published package would, instead of running the app.tsx.Loadchecks before running the package and returns the refusal to its caller. (Inside the child scope'sRun, a panic goes to the nearestcore.Catchinstead.)Sandbox.isolation(browser bool), tested on every target headless; a web-target test showsViewandLoadrefusing and the opted-in view running.examples/pluginsdoes not opt in: on the web it shows the refusal for both packages; its tests assert that on the web and skip the older-build test there (no build is ever accepted).docs/architecture/tsx.mdlists the exception in "The page's engine", the Sandbox and Signed packages sections and the departures table; the public TSX guide (en, zh) says how the web behaves and whatWebUnisolatedgives up; the roadmap gains "Packages isolated on the web".Left out: proposal item 2, real isolation on the web (an opaque-origin
sandbox="allow-scripts"iframe or a Worker, with the runtime's Go-backed APIs overpostMessage/MessageChannel). The browser engine'sValueAPI is synchronous and would need an asynchronous bridge for sandboxed realms, which is a separate project; it is recorded on the roadmap.Verification
go tool nsgo test ./tsxgo tool nsgo test -target web ./tsx(every sandbox, view and plugin test passes;TestDevToolsNameTSXLinesfails on the web onmaintoo)go tool nsgo test ./examples/pluginsgo tool nsgo test -target web ./examples/pluginsgo tool nsgo checkgo tool nsgo check -target allTestTheWebRefusesWhatItCannotIsolatefails on the web target without the check inSandbox.start(both packages run).Screenshots
None