Skip to content

Add "Disable browser WebSecurity" toggle to the preview browser#3766

Open
akarabach wants to merge 6 commits into
pingdotgg:mainfrom
akarabach:preview-disable-web-security
Open

Add "Disable browser WebSecurity" toggle to the preview browser#3766
akarabach wants to merge 6 commits into
pingdotgg:mainfrom
akarabach:preview-disable-web-security

Conversation

@akarabach

@akarabach akarabach commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

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-guest disablewebsecurity attribute 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 setting previewDisableWebSecurity (boolean, default false) in ClientSettingsSchema + ClientSettingsPatch. Persists to client-settings.json via 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 the disablewebsecurity attribute 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 (like partition/webpreferences) so it's present before the guest attaches.
  • apps/desktop/src/settings/DesktopClientSettings.test.ts — updates the round-trip ClientSettings fixture for the new field.

Behavior notes

  • Off by default; opt-in and persisted per machine.
  • Applies to preview tabs opened after toggling — Electron can't change web security on an already-attached guest, so existing tabs keep their mode until reopened.
  • Security posture is otherwise unchanged: sandbox/nodeIntegration/contextIsolation remain force-enforced by the will-attach-webview handler; only same-origin/CORS inside the guest is relaxed, and only on opt-in.
  • Also applies to agent-driven preview_* automation, since it uses the same webviews.

Testing

  • Typecheck + unit tests pass for @t3tools/contracts, @t3tools/web, and @t3tools/desktop; repo lint clean (no new warnings).
  • Manually verified in the desktop dev build: toggling the switch and opening a new preview tab enables/disables cross-origin fetch(); the setting survives app restart; a tab restored at startup honors the persisted value.

Note on the "true" as unknown as boolean cast in HostedBrowserWebview.tsx: disablewebsecurity is unknown to react-dom's runtime attribute registry, so React drops boolean values for it — the attribute must be a string to render, while React's types declare the prop as boolean. The cast is type-only; the runtime value stays the string "true", which Electron treats as presence.


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 (default false) in the contracts client-settings schema and patch type, wired through existing client-settings.json persistence.

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).

HostedBrowserWebview waits until client settings are hydrated, then freezes the value for that webview instance and passes Electron's disablewebsecurity attribute 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

  • Adds a previewDisableWebSecurity boolean to the client settings schema (defaulting to false) and exposes a "Disable browser WebSecurity" checkbox in PreviewMoreMenu.
  • When enabled, sets the disablewebsecurity attribute on the <webview> element in HostedBrowserWebview; the component defers rendering until settings are hydrated.
  • A toast notification informs the user that the change applies only to newly opened tabs, not existing ones.
  • Behavioral Change: toggling the setting has no effect on already-open preview tabs — only new tabs pick up the change.

Macroscope summarized 08af34e.

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.
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0d239483-ae45-4b17-98c2-4d60b4729afd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 7, 2026
Comment thread apps/web/src/browser/HostedBrowserWebview.tsx Outdated
@akarabach akarabach changed the title Add preview browser toggle to disable web security (CORS) Add "Disable browser WebSecurity" toggle to the preview browser Jul 7, 2026
@macroscopeapp

macroscopeapp Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

Comment thread apps/web/src/browser/HostedBrowserWebview.tsx Outdated
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.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread apps/web/src/browser/HostedBrowserWebview.tsx
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.
@akarabach akarabach force-pushed the preview-disable-web-security branch from 27ce2d2 to 29125ea Compare July 7, 2026 19:01
variant="switch"
closeOnClick={false}
checked={disableWebSecurity}
onCheckedChange={(checked) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant