Add "Disable browser WebSecurity" toggle to the preview browser#3766
Add "Disable browser WebSecurity" toggle to the preview browser#3766akarabach wants to merge 6 commits into
Conversation
Adds a per-machine client setting, previewDisableWebSecurity (default off), surfaced as a checkbox in the preview browser's three-dot menu. When enabled, preview <webview> tags render Electron's disablewebsecurity attribute, turning off the same-origin policy inside the guest page — the embedded-browser equivalent of Chrome's --disable-web-security. Applies to preview tabs opened after toggling.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review 2 blocking correctness issues found. This PR adds a security-related feature (disabling CORS in the preview browser) which warrants careful human review. Additionally, there are two unresolved review comments identifying potential race conditions around settings hydration that could cause bugs. You can customize Macroscope's approvability policy. Learn more. |
Gate the frozen disableWebSecurity value on client-settings hydration so a preview webview mounted during startup/session-restore reads the persisted setting instead of the default false snapshot. Subscribing via useClientSettingsHydrated both triggers hydration and re-renders on completion; the webview render is held until the value is frozen.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b0042ad. Configure here.
Use MenuCheckboxItem's switch variant so the toggle shows an always-visible on/off switch instead of a checkmark-only-when-checked row, and keep the menu open on toggle so it animates in place.
| }, [tabId, viewport._tag, viewportHeight, viewportWidth]); | ||
|
|
||
| if (!config) return null; | ||
| if (!config || disableWebSecurity === null) return null; |
There was a problem hiding this comment.
🟡 Medium browser/HostedBrowserWebview.tsx:189
The component returns null while disableWebSecurity === null, delaying the first <webview> render until client settings hydrate. initialSrc is frozen from the initial initialUrl at mount (line 46), so if navStatus.url changes during that delay — e.g. a restored tab finishes loading before hydration completes — the eventual <webview> still mounts with the stale src (about:blank or an old URL). Since registerWebview only calls loadURL for tabs still in Loading, a tab that already reached Success is never corrected and displays the wrong page. Consider not gating render on disableWebSecurity, or re-deriving initialSrc from the current initialUrl once hydration lands.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/browser/HostedBrowserWebview.tsx around line 189:
The component returns `null` while `disableWebSecurity === null`, delaying the first `<webview>` render until client settings hydrate. `initialSrc` is frozen from the initial `initialUrl` at mount (line 46), so if `navStatus.url` changes during that delay — e.g. a restored tab finishes loading before hydration completes — the eventual `<webview>` still mounts with the stale `src` (about:blank or an old URL). Since `registerWebview` only calls `loadURL` for tabs still in `Loading`, a tab that already reached `Success` is never corrected and displays the wrong page. Consider not gating render on `disableWebSecurity`, or re-deriving `initialSrc` from the current `initialUrl` once hydration lands.
The hydration gate delays the <webview> mount until previewDisableWebSecurity is captured. The registration effect keyed only on [config, tabId], so when the webview mounted on a later hydration-triggered render (config already resolved), registerWebview and its did-attach/dom-ready listeners never attached — breaking navigation and the desktop overlay for that tab. Add disableWebSecurity to the effect deps so it re-runs when the element finally mounts.
27ce2d2 to
29125ea
Compare
| variant="switch" | ||
| closeOnClick={false} | ||
| checked={disableWebSecurity} | ||
| onCheckedChange={(checked) => { |
There was a problem hiding this comment.
🟡 Medium preview/PreviewMoreMenu.tsx:146
If the user toggles the "Disable browser WebSecurity" switch before client settings finish hydrating at app startup, the change is silently lost and the checkbox reverts. updateClientSettings persists to the in-memory snapshot immediately, but the still-running hydration call then overwrites that snapshot with the stale on-disk value. Consider guarding updateClientSettings (or this toggle) so writes are blocked or queued until hydration completes, or guard the onCheckedChange handler with a hydration flag.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/preview/PreviewMoreMenu.tsx around line 146:
If the user toggles the "Disable browser WebSecurity" switch before client settings finish hydrating at app startup, the change is silently lost and the checkbox reverts. `updateClientSettings` persists to the in-memory snapshot immediately, but the still-running hydration call then overwrites that snapshot with the stale on-disk value. Consider guarding `updateClientSettings` (or this toggle) so writes are blocked or queued until hydration completes, or guard the `onCheckedChange` handler with a hydration flag.

What & why
Adds an opt-in control to run t3code's built-in preview browser with web security (the same-origin policy / CORS) disabled — the embedded-browser equivalent of launching Chrome with
--disable-web-security. A local-development escape hatch for previewing pages that call cross-origin APIs without CORS headers.t3code's preview is an embedded Electron
<webview>(in-process Chromium), not a launched browser, so this is delivered via Electron's per-guestdisablewebsecurityattribute rather than a browser binary/flags.Screenshots
Preview menu — WebSecurity on (switch enabled) + confirmation toast
Preview menu — WebSecurity off (default, switch off)
Changes
packages/contracts/src/settings.ts— new client settingpreviewDisableWebSecurity(boolean, defaultfalse) inClientSettingsSchema+ClientSettingsPatch. Persists toclient-settings.jsonvia existing patch routing.apps/web/src/components/preview/PreviewMoreMenu.tsx— a "Disable browser WebSecurity" switch in the preview browser's three-dot menu (MenuCheckboxItem variant="switch"), which persists the setting and toasts that the change applies to newly opened tabs.apps/web/src/browser/HostedBrowserWebview.tsx— renders thedisablewebsecurityattribute on the preview<webview>when enabled. The value is captured once per webview (Electron reads it only at guest attach), after client-settings hydration completes so startup/session-restore tabs honor the persisted value; rendered as a JSX attribute (likepartition/webpreferences) so it's present before the guest attaches.apps/desktop/src/settings/DesktopClientSettings.test.ts— updates the round-tripClientSettingsfixture for the new field.Behavior notes
sandbox/nodeIntegration/contextIsolationremain force-enforced by thewill-attach-webviewhandler; only same-origin/CORS inside the guest is relaxed, and only on opt-in.preview_*automation, since it uses the same webviews.Testing
@t3tools/contracts,@t3tools/web, and@t3tools/desktop; repo lint clean (no new warnings).fetch(); the setting survives app restart; a tab restored at startup honors the persisted value.Note
Medium Risk
Opt-in relaxation of same-origin/CORS in the embedded preview guest is security-sensitive, though scoped to local dev preview webviews and fixed at guest attach time.
Overview
Adds an opt-in, persisted client setting
previewDisableWebSecurity(defaultfalse) in the contracts client-settings schema and patch type, wired through existingclient-settings.jsonpersistence.The preview three-dot menu gets a "Disable browser WebSecurity" switch that saves the setting and shows a toast that only newly opened preview tabs pick up the change (existing tabs must be reopened).
HostedBrowserWebviewwaits until client settings are hydrated, then freezes the value for that webview instance and passes Electron'sdisablewebsecurityattribute when enabled (string"true"so React does not drop the unknown attribute). Rendering is deferred until hydration completes so session-restore tabs use the persisted value, not the pre-hydration default.Desktop client-settings tests update the round-trip fixture for the new field.
Reviewed by Cursor Bugbot for commit 08af34e. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add "Disable browser WebSecurity" toggle to the preview browser
previewDisableWebSecurityboolean to the client settings schema (defaulting tofalse) and exposes a "Disable browser WebSecurity" checkbox inPreviewMoreMenu.disablewebsecurityattribute on the<webview>element inHostedBrowserWebview; the component defers rendering until settings are hydrated.Macroscope summarized 08af34e.