Skip to content

Remove preview iframe allow-same-origin with diagnostics and auto-render performance parity #67

@knightedcodemonkey

Description

@knightedcodemonkey

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:

  1. diagnostics reporting degraded
  2. auto-render performance regressed

This issue implements a phased migration to restore isolation while preserving diagnostics quality and render throughput.

Problem Statement

  1. Current sandbox model is flagged by the browser and can undermine origin isolation.
  2. Parent currently mutates iframe document directly via contentDocument in iframe-preview-executor.js, which is incompatible with strict sandboxing.
  3. Runtime graph and bootstrap flow need to work without parent document access.
  4. Existing diagnostics and rendering behavior must remain stable.

Goals

  1. Remove allow-same-origin from preview sandbox.
  2. Eliminate parent direct iframe DOM access for runtime bootstrapping.
  3. Preserve runtime diagnostics behavior and fidelity in preview panel.
  4. Keep auto-render responsiveness within agreed performance budget.
  5. Keep CDN-first runtime behavior unchanged for end users.

Non-Goals

  1. No broad refactor of editor/workspace tabs.
  2. No redesign of diagnostics UI.
  3. No new backend/service dependency.

Exact Implementation Plan

Phase 0: Baseline and guardrails

  1. 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.
  2. Capture baseline metrics for:
    1. first render latency
    2. median auto-render latency over 20 edits
    3. p95 auto-render latency
    4. diagnostics arrival latency after known runtime error
  3. Save benchmark script/instructions in docs for repeatable comparison.

Phase 1: Protocol extraction without behavior change

  1. Introduce a message protocol module for parent/iframe events in a new runtime protocol file under preview-runtime.
  2. Move all parent-to-iframe configuration into one payload:
    1. mode
    2. entry specifier
    3. runtime specifiers
    4. css text
    5. padding
    6. background color
    7. import map or module manifest
  3. Keep allow-same-origin temporarily in this phase to reduce blast radius while validating protocol parity.
  4. Ensure current emitted messages remain backward-compatible:
    1. rendered
    2. runtime-error
    3. execution error path

Phase 2: Remove parent contentDocument dependency

  1. Replace parent doc.open/doc.write/contentDocument mutation flow in iframe-preview-executor.js with one of:
    1. srcdoc bootstrap shell
    2. blob-based shell URL
  2. Ensure bootstrap shell owns:
    1. style injection
    2. module import setup
    3. error listeners
    4. render start
  3. Parent communicates only through postMessage, no direct iframe DOM writes after creation.
  4. Background color and host padding are applied inside iframe from received config.

Phase 3: Isolate runtime origin

  1. Remove allow-same-origin from sandbox attribute in iframe-preview-executor.js.
  2. Keep allow-scripts and only required additional sandbox permissions.
  3. Validate that preview code cannot access parent storage/DOM in practical checks.
  4. Keep an emergency local feature flag for temporary rollback during validation window only, then remove.

Phase 4: Performance recovery and optimization

  1. If regression appears, optimize without reintroducing allow-same-origin:
    1. avoid full shell rebuild when not needed
    2. cache stable runtime specifier metadata
    3. send minimal delta payloads for rerender
  2. Re-run baseline benchmark suite and compare against Phase 0.
  3. Confirm performance budget is met.

Acceptance Criteria

  1. Sandbox no longer includes allow-same-origin in iframe-preview-executor.js.
  2. No parent direct access to iframe contentDocument for runtime setup in iframe-preview-executor.js.
  3. Diagnostics parity:
    1. runtime errors still show message, source/origin, and module context
    2. post-render runtime error path still reaches preview panel
  4. Performance budget:
    1. median auto-render latency regression no worse than 10 percent vs baseline
    2. p95 auto-render latency regression no worse than 15 percent vs baseline
  5. Browser warning about allow-scripts plus allow-same-origin no longer appears.
  6. Relevant automated tests pass and targeted manual checks pass.

Test Plan

  1. Unit/integration:
    1. add tests for message protocol encode/decode and event filtering
    2. add tests for iframe executor success, pre-render failure, post-render runtime failure
  2. E2E:
    1. existing rendering mode specs
    2. existing runtime error recovery specs
    3. new security assertion that preview cannot read parent token/state
  3. Manual:
    1. dark/light theme preview rendering
    2. rapid typing auto-render responsiveness
    3. runtime error then source fix recovery cycle
  4. Commands:
    1. npm run lint
    2. npm run build
    3. targeted playwright specs for preview/runtime flows

Risks and Mitigations

  1. Diagnostics loss:
    1. mitigate with protocol contract tests and parity snapshots
  2. Performance regression:
    1. mitigate with baseline instrumentation and staged optimization
  3. Blob/module loading incompatibilities in opaque origin:
    1. mitigate by generating executable module artifacts within iframe runtime context from payload, not via parent DOM mutation

Rollout

  1. Land in small PR sequence:
    1. protocol extraction
    2. bootstrap decoupling
    3. sandbox tightening
    4. perf tuning and cleanup
  2. Validate each PR with targeted e2e before merging next step.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions