Skip to content

[Bug]: Injected iframe cleanup on error #1627

Open
@lapritchett

Description

Preflight Checklist

  • I have searched the issue tracker for a bug report that matches the one I want to file, without success.

What package is this bug report for?

Other (specify below)

Version

v2.0.0-alpha.18

Expected Behavior

@rrweb/record and rrweb-snapshot should clean up any iframes that are injected for the purposes of getting clean prototypes.

Actual Behavior

In @rrweb/record and rrweb-snapshot, getUntaintedPrototype contains code that injects an iframe but doesn't always properly clean it up (in my case, when calling const win = iframeEl.contentWindow; doesn't work within an iframe):

  try {
    const iframeEl = document.createElement("iframe");
    document.body.appendChild(iframeEl);
    const win = iframeEl.contentWindow;
    if (!win) return defaultObj.prototype;
    const untaintedObject = win[key].prototype;
    document.body.removeChild(iframeEl);
    if (!untaintedObject) return defaultPrototype;
    return untaintedBasePrototype[key] = untaintedObject;
  } catch {
    return defaultPrototype;
  }

Steps to Reproduce

Enable @rrweb/record
Go to https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
Paste the following into the HTML input:

<iframe
  id="inlineFrameExample"
  title="Inline Frame Example"
  width="300"
  name="Example Website"
  src="https://example.com"
  title="Example.com"
  ref={iframeRef}
  sandbox="allow-scripts"
  height="200"
  src="">
</iframe>

Click on "Example Domain" when it renders in the iframe
Note a new blank iframe is injected and not removed
CleanShot 2025-01-14 at 18 16 42@2x

Testcase Gist URL

No response

Additional Information

I suggest adding a finally clause to handle the iframe cleanup:

  let iframeEl;
try {
  iframeEl = document.createElement("iframe");
  document.body.appendChild(iframeEl);
  const win = iframeEl.contentWindow;
  if (!win) return defaultObj.prototype;
  const untaintedObject = win[key].prototype;
  if (!untaintedObject) return defaultPrototype;
  return untaintedBasePrototype[key] = untaintedObject;
} catch {
  return defaultPrototype;
} finally {
  try {
    if (iframeEl && iframeEl.parentNode) {
      document.body.removeChild(iframeEl);
    }
  } catch (cleanupErr) {
    console.error('[getUntaintedPrototype2] cleanup error', cleanupErr);
  }
}

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions