sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.66k stars 4.13k forks source link

Element resize listening via <iframe> causes cryptic SecurityErrors #13476

Open gabrielecirulli opened 5 days ago

gabrielecirulli commented 5 days ago

Describe the bug

I am encountering this error in my Sentry logs:

SecurityError: Blocked a frame with origin "https://play2048.co" from accessing a cross-origin frame. Protocols, domains, and ports must match.

A snapshot of the error is available here.

Here's what I've found so far:

Link to the relevant function at the version I'm using (4.2.19)

I have ads on my page and I suspect some third-party script is intercepting these <iframe>s and altering their src.

Changing the src of these <iframe>s would re-trigger the onload function defined by svelte, repeating the call to iframe.contentWindow.addEventListener("resize"), which is likely undesired.

A potential mitigation could be removing the onload listener as soon as the "resize" event listener is added. In my testing with the snippet included below this seems to prevent the CSP error, but I'm unsure if the resize listener persists after the iframe src changes.

Potentially related article: https://help.noibu.com/hc/en-us/articles/4413414445069-Blocked-a-frame-with-origin-xyz-from-accessing-a-cross-origin-frame

Reproduction

const iframe = document.createElement("iframe");
iframe.src = "about:blank";

iframe.onload = function () {
  console.log("onload fired");

  iframe.contentWindow.addEventListener("resize", function () {
    console.log("iframe resized");
  });

  // iframe.onload = undefined; // uncomment this and the CSP error will be gone
};

document.body.appendChild(iframe);

setTimeout(() => {
  iframe.src = "https://example.com";
}, 2000);

Logs

https://2048-ip-holding-bv.sentry.io/share/issue/0b2c4cdbea0d4adaaba28ba66003c584

System Info

System:
    OS: macOS 14.6.1
    CPU: (8) arm64 Apple M2
    Memory: 61.81 MB / 24.00 GB
    Shell: 3.6.1 - /opt/homebrew/bin/fish
  Binaries:
    Node: 22.7.0 - ~/.local/share/nvm/v22.7.0/bin/node
    Yarn: 1.22.21 - /opt/homebrew/bin/yarn
    npm: 10.8.2 - ~/.local/share/nvm/v22.7.0/bin/npm
    pnpm: 8.9.2 - /opt/homebrew/bin/pnpm
    bun: 1.1.22 - ~/.bun/bin/bun
  Browsers:
    Chrome: 129.0.6668.70
    Chrome Canary: 131.0.6751.0
    Firefox Nightly: 128.0a1
    Safari: 17.6
    Safari Technology Preview: 18.0
  npmPackages:
    svelte: ^4.2.19 => 4.2.19 

Severity

annoyance

gabrielecirulli commented 5 days ago

The following snippet is also a useful test to run on play2048.co:

 Array.from(document.querySelectorAll('iframe')).filter(
  (iframe) => iframe.src === 'about:blank'
).forEach((iframe, i) => {
  console.log('updating iframe', i);
  iframe.src = 'https://example.com';
});

When I do the above, the CSP error appears and any resize observers that depend on those iframes stop working. The UI is still responsive but layout breaks.

An alternative approach if we want to keep the listeners intact may be to listen to onload and check src and if it's not about:blank, to immediately reset it to about:blank and bail. Further calls to onload would then be able to safely hook into the iframe because the src is fixed.

Conduitry commented 5 days ago

Alternatively, Svelte 5 is switching to using ResizeObserver for these bindings - https://svelte-5-preview.vercel.app/docs/breaking-changes#modern-browser-required - At this point, I'm not sure we want to put much effort into the iframes mechanism.

gabrielecirulli commented 5 days ago

Might still be nice to prevent such CSP errors if there’s a simple way to do so. If anything it might prevent Sentry error quotas being blown up :)

gabrielecirulli commented 4 days ago

I deployed the same Svelte code with additional console.log statements directly in the runtime source, but it shows the src hasn’t changed when the callback triggers, even though the SecurityError is still thrown.

CleanShot 2024-10-02 at 10 24 13@2x