Skip to content

Sandbox CLI product scripts by default - #829

Open
pgherveou wants to merge 64 commits into
mainfrom
network-permissions/03-cli
Open

pgherveou wants to merge 64 commits into
mainfrom
network-permissions/03-cli

Conversation

@pgherveou

@pgherveou pgherveou commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator
  • Run --script products in the shared browser sandbox, authorizing fetch, XHR and WebSockets through authorize_remote_permission, preserving native redirect behavior.
  • Show the requested domains or capability and preserve Allow once, Allow always and Deny in CLI prompts.
  • Add truapi-host install-browser and explicit --trusted-script for Bun diagnostics; migrate existing diagnostic callers.
  • Ship the shared container, browser assets and dependencies through CLI packaging, installation and updates.

test-truapi-3rd-party-fetch against a CLI host built from this branch:

Run Approved Refused
Sandboxed --script product One prompt, then two fetches at HTTP 200 The denial blocking every later request

truapi-host dev --app-port 3000 -- bun run dev --port 3000, with the development-only bridge tag in index.html:

Host Product
The dev host approving the Remote request The product in a browser tab reporting the grant

Authored with assistance from Codex.

@pgherveou
pgherveou added this pull request to stack #830 September 17, 2026 15:51
@github-actions github-actions Bot added documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code labels Sep 17, 2026
@pgherveou pgherveou changed the title network permissions/03 cli Sandbox CLI product scripts by default Sep 17, 2026
@socket-security

socket-security Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​esbuild-wasm@​0.28.1991007587100
Updatednpm/​@​parity/​truapi@​0.17.0 ⏵ 0.17.08910010099100

View full report

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

CI Status: 21 required jobs green, 20 passed and 1 skipped by path filter.

All job results
job result
android-bindings success
changes success
changeset-guard success
cli-package success
cli-sandbox success
codegen success
e2e skipped
explorer success
host-android-bindings success
ios-bindings success
ios-swift success
licenses success
playground success
provider-android-bindings success
release-guard success
rust success
ts-client success
ts-debugger success
ts-host success
wasm-provider success
workflow-lint success

Commit 7f0066ee · run log

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

iOS simulator preview

Built from 7f0066eeb, stamped with it in TrUAPICommit.

gh run download 35448513385 --name simulator-preview-7f0066eeb
unzip polkadot-app-*.app.zip
xcrun simctl install booted polkadot-app.app
xcrun simctl launch booted io.parity.polkadotapp.develop

Or download it in a browser, which arrives as a zip wrapping
the .app.zip, so it needs unzipping twice.

An arm64 simulator slice, so it needs an Apple Silicon Mac and does not
install on a device. Kept for 14 days, after which the link stops
resolving and a new push rebuilds it.

@filvecchiato

Copy link
Copy Markdown
Collaborator
  1. Redirects bypass the allowlist, and here there's a hook to fix it. sandbox-runner.ts:172 records {url, authorization} only when !operations.has(event.requestId). A redirect leg re-fires Network.requestWillBeSent with the same requestId, so operation.url keeps the original host and operation.authorization stays resolved; the paused leg finds the cached grant and continues. sandbox-runner.test.ts:170 proves it — the callback allows only 127.0.0.1, the 302 targets localhost, and authorizations has one entry. Fix is small: on a requestWillBeSent carrying redirectResponse, refresh operation.url and clear operation.authorization. This is the third place in the stack where a granted host launders access to an ungranted one — worth settling once across the stack rather than per PR.

  2. The authorization decision is stubbed in-realm. truapi_network_intent (sandbox-runner.ts:283) returns granted: true for any well-formed Remote request without consulting the user or Rust. Intentional and commented — the CDP layer owns the grant, so the in-realm request must not consume one. The gate logic still runs, but its deny path never fires, so the CLI suite doesn't cover it. Please state that in SPEC.md so nobody reads these tests as container coverage.

  3. installWebSocketBackend.toString() is unguarded against packaging. sandbox-runner.ts:359 serializes a function from another module into the page. Self-contained today and build-cli-runner.ts doesn't minify, but nothing enforces either. cli-runner-package.test.ts does exercise the packaged runner — it just never reaches the page-side path (the runner exits on the missing TRUAPI_FRAME_URL). One packaged-runner test that completes a WebSocket round trip would close it.

  4. TRUAPI_SCRIPT_MODE is inherited from the ambient environment. Rust never sets it; runner.ts:41 reads process.env. scripts/battery.sh:42 already does export TRUAPI_SCRIPT_MODE=trusted, so everything downstream of that script loses the sandbox — and a stale shell export or CI env block does the same. There's a stderr warning but no CLI surface. A --trusted flag puts the choice at the call site.

  5. grantPermissions(["local-network-access"]) is unconditional (:156). With hostname-only authorization, approving 127.0.0.1 grants every service on the developer's machine. Fine for a dev tool; the prompt should probably say more than the bare hostname.

  6. CSP. 'unsafe-eval' (:91) with a pre-bundled product — what needs it? connect-src/img-src are dead since CDP denies anyway, but they read as policy.

  7. Changeset. "@parity/truapi": patch for a change that flips the default execution model of --script — scripts using node:, Bun. or the filesystem now fail at build time. Minor at least, naming the breaking default; js/container isn't mentioned even though its bundle now ships in the archive.

Minor: isFrame and the WebSocket send validator accept 64 MiB arrays that cross exposeBinding/page.evaluate as JSON number arrays; runSandboxScript hardcodes the 300 s timeout; browser.close() in finally (:414) can mask the script error; networkRequest(networkId).ready hangs to the script timeout if requestWillBeSent never arrives, rather than failing closed.

@pgherveou

pgherveou commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the review and Lore.

  • Keep normal redirects after the initial permission check so CLI behavior matches native hosts.
  • Keep permission checks outside the page so product code cannot approve its own requests.
  • Require --trusted-script so an environment variable cannot silently disable sandboxing.
  • Name the requested access and preserve all three permission choices so the CLI respects the user's decision.
  • Retain local-network access so Rust-approved connections to local services work.
  • Block JavaScript eval while keeping WebAssembly crypto working.
  • Test packaged WebSockets to catch failures that source tests would miss.
  • Update CI and release notes for the new sandbox runtime requirements.
  • Preserve the original script error so cleanup failures cannot hide the cause.

Kept existing timeouts and browser environment settings: requests already fail closed, esbuild supplies NODE_ENV, and Chromium uses a temporary profile. Memory limits are follow-up #847.

Codex-assisted.

Base automatically changed from network-permissions/02-sandbox to main September 19, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants