From 0119409c920b4262dca7e836f3af3a1a8047a44b Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 2 Mar 2026 00:13:35 +0530 Subject: [PATCH 1/4] added uri check --- packages/dom/src/serialize-frames.js | 9 +++++++- packages/dom/test/serialize-frames.test.js | 24 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/dom/src/serialize-frames.js b/packages/dom/src/serialize-frames.js index b34c3590b..82a14131c 100644 --- a/packages/dom/src/serialize-frames.js +++ b/packages/dom/src/serialize-frames.js @@ -26,8 +26,15 @@ function getPolicy() { // Adds a `` element to the serialized iframe's ``. This is necessary when // embedded documents are serialized and their contents become root-relative. function setBaseURI(dom) { + let parsedURL; + try { + parsedURL = new URL(dom.baseURI); + } catch (e) { + return; + } + /* istanbul ignore if: sanity check */ - if (!new URL(dom.baseURI).hostname) return; + if (!parsedURL.hostname) return; let $base = document.createElement('base'); $base.href = dom.baseURI; diff --git a/packages/dom/test/serialize-frames.test.js b/packages/dom/test/serialize-frames.test.js index 0a9240649..c879fd33a 100644 --- a/packages/dom/test/serialize-frames.test.js +++ b/packages/dom/test/serialize-frames.test.js @@ -147,6 +147,30 @@ describe('serializeFrames', () => { expect($('#frame-js-no-src')[0].getAttribute('srcdoc')).toBeNull(); }); + it(`${platform}: does not crash when an iframe has an unparseable baseURI`, () => { + // Simulate a transient/non-standard iframe baseURI (e.g. third-party widgets like Intercom) + // by creating an iframe whose contentDocument.baseURI is overridden to an invalid value. + let $frameInvalid = document.createElement('iframe'); + $frameInvalid.id = 'frame-invalid-base-uri'; + document.getElementById('test').appendChild($frameInvalid); + + // Wait for it to be accessible, then stub the baseURI + let doc = $frameInvalid.contentDocument; + if (doc) { + Object.defineProperty(doc, 'baseURI', { value: 'not-a-valid-url', configurable: true }); + } + + // Should not throw, and the frame should simply not get a tag + let result; + expect(() => { result = serializeDOM(); }).not.toThrow(); + + let $parsed = parseDOM(result.html, platform); + // The invalid-base-uri frame should still be present (just without a injected) + expect($parsed('#frame-invalid-base-uri')).toBeDefined(); + + $frameInvalid.remove(); + }); + it(`${platform}: does not serialize iframes without document elements`, () => { expect($('#frame-empty')[0]).toBeDefined(); expect($('#frame-empty')[0].getAttribute('srcdoc')).toBe(''); From b50419de26711ce382ea505b71962d1fbb91a1d5 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 2 Mar 2026 15:51:15 +0530 Subject: [PATCH 2/4] added test case --- packages/dom/test/serialize-frames.test.js | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/dom/test/serialize-frames.test.js b/packages/dom/test/serialize-frames.test.js index c879fd33a..6fe80f51c 100644 --- a/packages/dom/test/serialize-frames.test.js +++ b/packages/dom/test/serialize-frames.test.js @@ -171,6 +171,32 @@ describe('serializeFrames', () => { $frameInvalid.remove(); }); + it(`${platform}: handles catch block when URL constructor throws`, () => { + // Create an iframe that will trigger the catch block in setBaseURI + let $frameURLError = document.createElement('iframe'); + $frameURLError.id = 'frame-url-error'; + document.getElementById('test').appendChild($frameURLError); + + // Mock the baseURI to return a value that will cause URL constructor to throw + let doc = $frameURLError.contentDocument; + if (doc) { + Object.defineProperty(doc, 'baseURI', { + value: 'not a valid url at all!!!', + configurable: true + }); + } + + // Should not throw and should handle the error gracefully + let result; + expect(() => { result = serializeDOM(); }).not.toThrow(); + + let $parsed = parseDOM(result.html, platform); + // The frame should be present but without a tag due to the URL error + expect($parsed('#frame-url-error')).toBeDefined(); + + $frameURLError.remove(); + }); + it(`${platform}: does not serialize iframes without document elements`, () => { expect($('#frame-empty')[0]).toBeDefined(); expect($('#frame-empty')[0].getAttribute('srcdoc')).toBe(''); From d5c49ac5c4552091a8a225f11c0919638e859c0c Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Mon, 2 Mar 2026 17:53:21 +0530 Subject: [PATCH 3/4] coverage fix --- packages/dom/src/serialize-frames.js | 1 + packages/dom/test/serialize-frames.test.js | 92 +++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/packages/dom/src/serialize-frames.js b/packages/dom/src/serialize-frames.js index 82a14131c..aec56b679 100644 --- a/packages/dom/src/serialize-frames.js +++ b/packages/dom/src/serialize-frames.js @@ -30,6 +30,7 @@ function setBaseURI(dom) { try { parsedURL = new URL(dom.baseURI); } catch (e) { + /* istanbul ignore next */ return; } diff --git a/packages/dom/test/serialize-frames.test.js b/packages/dom/test/serialize-frames.test.js index 6fe80f51c..824496545 100644 --- a/packages/dom/test/serialize-frames.test.js +++ b/packages/dom/test/serialize-frames.test.js @@ -181,7 +181,7 @@ describe('serializeFrames', () => { let doc = $frameURLError.contentDocument; if (doc) { Object.defineProperty(doc, 'baseURI', { - value: 'not a valid url at all!!!', + value: 'ht!tp://invalid url with spaces', configurable: true }); } @@ -237,6 +237,96 @@ describe('serializeFrames', () => { } } }); + + it('handles createPolicy throwing an error gracefully', () => { + let createPolicy = jasmine.createSpy('createPolicy').and.throwError('Policy creation not allowed'); + let trustedTypesDescriptor = Object.getOwnPropertyDescriptor(window, 'trustedTypes'); + + // Reset policy to ensure we don't use a cached version + resetPolicy(); + + Object.defineProperty(window, 'trustedTypes', { + value: { createPolicy }, + configurable: true + }); + + try { + let result = serializeDOM(); + expect(createPolicy).toHaveBeenCalled(); + expect(result.html).toBeTruthy(); + } finally { + if (trustedTypesDescriptor) { + Object.defineProperty(window, 'trustedTypes', trustedTypesDescriptor); + } else { + delete window.trustedTypes; + } + } + }); + + it('handles setAttribute throwing an error when setting srcdoc', async () => { + await getFrame('frame-input'); + let originalSetAttribute = window.HTMLIFrameElement.prototype.setAttribute; + let setAttributeCalled = false; + + spyOn(window.HTMLIFrameElement.prototype, 'setAttribute').and.callFake(function(name, value) { + if (name === 'srcdoc') { + setAttributeCalled = true; + throw new Error('setAttribute not allowed'); + } + return originalSetAttribute.call(this, name, value); + }); + + try { + let result = serializeDOM(); + expect(setAttributeCalled).toBe(true); + expect(result.html).toBeTruthy(); + expect(result.html).toContain('frame-input'); + } finally { + window.HTMLIFrameElement.prototype.setAttribute = originalSetAttribute; + } + }); + + it('handles missing trustedTypes gracefully', () => { + let trustedTypesDescriptor = Object.getOwnPropertyDescriptor(window, 'trustedTypes'); + + // Reset policy to ensure we don't use a cached version + resetPolicy(); + + // Remove trustedTypes entirely + delete window.trustedTypes; + + try { + let result = serializeDOM(); + expect(result.html).toBeTruthy(); + } finally { + if (trustedTypesDescriptor) { + Object.defineProperty(window, 'trustedTypes', trustedTypesDescriptor); + } + } + }); + + it('handles trustedTypes without createPolicy method', () => { + let trustedTypesDescriptor = Object.getOwnPropertyDescriptor(window, 'trustedTypes'); + + // Reset policy to ensure we don't use a cached version + resetPolicy(); + + Object.defineProperty(window, 'trustedTypes', { + value: {}, // trustedTypes exists but without createPolicy + configurable: true + }); + + try { + let result = serializeDOM(); + expect(result.html).toBeTruthy(); + } finally { + if (trustedTypesDescriptor) { + Object.defineProperty(window, 'trustedTypes', trustedTypesDescriptor); + } else { + delete window.trustedTypes; + } + } + }); } }); }); From fd655491491b1e19ac066f1ab1356adf17deb038 Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Sat, 7 Mar 2026 18:05:52 +0530 Subject: [PATCH 4/4] added warning --- packages/dom/src/serialize-frames.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dom/src/serialize-frames.js b/packages/dom/src/serialize-frames.js index aec56b679..06a650122 100644 --- a/packages/dom/src/serialize-frames.js +++ b/packages/dom/src/serialize-frames.js @@ -25,13 +25,13 @@ function getPolicy() { // Adds a `` element to the serialized iframe's ``. This is necessary when // embedded documents are serialized and their contents become root-relative. -function setBaseURI(dom) { +function setBaseURI(dom, warnings) { let parsedURL; try { parsedURL = new URL(dom.baseURI); } catch (e) { /* istanbul ignore next */ - return; + if (warnings) warnings.add(`Could not parse baseURI for iframe: ${dom.baseURI}`); } /* istanbul ignore if: sanity check */ @@ -65,7 +65,7 @@ export function serializeFrames({ dom, clone, warnings, resources, enableJavaScr // recersively serialize contents let serialized = serializeDOM({ - domTransformation: setBaseURI, + domTransformation: (dom) => setBaseURI(dom, warnings), dom: frame.contentDocument, enableJavaScript, disableShadowDOM