Summary
The preview iframe currently uses a sandbox that includes allow-same-origin and allow-scripts in iframe-preview-executor.js, which triggers browser security warnings and weakens isolation for user-authored preview code.
We previously saw regressions when removing allow-same-origin:
- diagnostics reporting degraded
- auto-render performance regressed
This issue implements a phased migration to restore isolation while preserving diagnostics quality and render throughput.
Problem Statement
- Current sandbox model is flagged by the browser and can undermine origin isolation.
- Parent currently mutates iframe document directly via contentDocument in iframe-preview-executor.js, which is incompatible with strict sandboxing.
- Runtime graph and bootstrap flow need to work without parent document access.
- Existing diagnostics and rendering behavior must remain stable.
Goals
- Remove allow-same-origin from preview sandbox.
- Eliminate parent direct iframe DOM access for runtime bootstrapping.
- Preserve runtime diagnostics behavior and fidelity in preview panel.
- Keep auto-render responsiveness within agreed performance budget.
- Keep CDN-first runtime behavior unchanged for end users.
Non-Goals
- No broad refactor of editor/workspace tabs.
- No redesign of diagnostics UI.
- No new backend/service dependency.
Exact Implementation Plan
Phase 0: Baseline and guardrails
- Add lightweight timing instrumentation around render pipeline start, iframe ready, rendered event, and runtime error event in render-runtime.js and iframe-preview-executor.js.
- Capture baseline metrics for:
- first render latency
- median auto-render latency over 20 edits
- p95 auto-render latency
- diagnostics arrival latency after known runtime error
- Save benchmark script/instructions in docs for repeatable comparison.
Phase 1: Protocol extraction without behavior change
- Introduce a message protocol module for parent/iframe events in a new runtime protocol file under preview-runtime.
- Move all parent-to-iframe configuration into one payload:
- mode
- entry specifier
- runtime specifiers
- css text
- padding
- background color
- import map or module manifest
- Keep allow-same-origin temporarily in this phase to reduce blast radius while validating protocol parity.
- Ensure current emitted messages remain backward-compatible:
- rendered
- runtime-error
- execution error path
Phase 2: Remove parent contentDocument dependency
- Replace parent doc.open/doc.write/contentDocument mutation flow in iframe-preview-executor.js with one of:
- srcdoc bootstrap shell
- blob-based shell URL
- Ensure bootstrap shell owns:
- style injection
- module import setup
- error listeners
- render start
- Parent communicates only through postMessage, no direct iframe DOM writes after creation.
- Background color and host padding are applied inside iframe from received config.
Phase 3: Isolate runtime origin
- Remove allow-same-origin from sandbox attribute in iframe-preview-executor.js.
- Keep allow-scripts and only required additional sandbox permissions.
- Validate that preview code cannot access parent storage/DOM in practical checks.
- Keep an emergency local feature flag for temporary rollback during validation window only, then remove.
Phase 4: Performance recovery and optimization
- If regression appears, optimize without reintroducing allow-same-origin:
- avoid full shell rebuild when not needed
- cache stable runtime specifier metadata
- send minimal delta payloads for rerender
- Re-run baseline benchmark suite and compare against Phase 0.
- Confirm performance budget is met.
Acceptance Criteria
- Sandbox no longer includes allow-same-origin in iframe-preview-executor.js.
- No parent direct access to iframe contentDocument for runtime setup in iframe-preview-executor.js.
- Diagnostics parity:
- runtime errors still show message, source/origin, and module context
- post-render runtime error path still reaches preview panel
- Performance budget:
- median auto-render latency regression no worse than 10 percent vs baseline
- p95 auto-render latency regression no worse than 15 percent vs baseline
- Browser warning about allow-scripts plus allow-same-origin no longer appears.
- Relevant automated tests pass and targeted manual checks pass.
Test Plan
- Unit/integration:
- add tests for message protocol encode/decode and event filtering
- add tests for iframe executor success, pre-render failure, post-render runtime failure
- E2E:
- existing rendering mode specs
- existing runtime error recovery specs
- new security assertion that preview cannot read parent token/state
- Manual:
- dark/light theme preview rendering
- rapid typing auto-render responsiveness
- runtime error then source fix recovery cycle
- Commands:
- npm run lint
- npm run build
- targeted playwright specs for preview/runtime flows
Risks and Mitigations
- Diagnostics loss:
- mitigate with protocol contract tests and parity snapshots
- Performance regression:
- mitigate with baseline instrumentation and staged optimization
- Blob/module loading incompatibilities in opaque origin:
- mitigate by generating executable module artifacts within iframe runtime context from payload, not via parent DOM mutation
Rollout
- Land in small PR sequence:
- protocol extraction
- bootstrap decoupling
- sandbox tightening
- perf tuning and cleanup
- Validate each PR with targeted e2e before merging next step.
Summary
The preview iframe currently uses a sandbox that includes allow-same-origin and allow-scripts in iframe-preview-executor.js, which triggers browser security warnings and weakens isolation for user-authored preview code.
We previously saw regressions when removing allow-same-origin:
This issue implements a phased migration to restore isolation while preserving diagnostics quality and render throughput.
Problem Statement
Goals
Non-Goals
Exact Implementation Plan
Phase 0: Baseline and guardrails
Phase 1: Protocol extraction without behavior change
Phase 2: Remove parent contentDocument dependency
Phase 3: Isolate runtime origin
Phase 4: Performance recovery and optimization
Acceptance Criteria
Test Plan
Risks and Mitigations
Rollout