From 5689884c8e32cda6a802ac36b4d23218f67b38ed Mon Sep 17 00:00:00 2001 From: clay jenson Date: Wed, 5 Nov 2025 16:53:58 +0800 Subject: [PATCH] fix(runtime-dom): ensure iframe sandbox is handled as an attribute to prevent unintended behavior (#13950) close #13946 --- .../runtime-dom/__tests__/patchAttrs.spec.ts | 34 +++++++++++++++++++ packages/runtime-dom/src/patchProp.ts | 7 ++++ 2 files changed, 41 insertions(+) diff --git a/packages/runtime-dom/__tests__/patchAttrs.spec.ts b/packages/runtime-dom/__tests__/patchAttrs.spec.ts index 393b685b0e..d181a1b038 100644 --- a/packages/runtime-dom/__tests__/patchAttrs.spec.ts +++ b/packages/runtime-dom/__tests__/patchAttrs.spec.ts @@ -88,4 +88,38 @@ describe('runtime-dom: attrs patching', () => { expect(el2.dataset.test).toBe(undefined) expect(testvalue).toBe(obj) }) + + // #13946 + test('sandbox should be handled as attribute even if property exists', () => { + const iframe = document.createElement('iframe') as any + let propSetCount = 0 + // simulate sandbox property in jsdom environment + Object.defineProperty(iframe, 'sandbox', { + configurable: true, + enumerable: true, + get() { + return this._sandbox + }, + set(v) { + propSetCount++ + this._sandbox = v + }, + }) + + patchProp(iframe, 'sandbox', null, 'allow-scripts') + expect(iframe.getAttribute('sandbox')).toBe('allow-scripts') + expect(propSetCount).toBe(0) + + patchProp(iframe, 'sandbox', 'allow-scripts', null) + expect(iframe.hasAttribute('sandbox')).toBe(false) + expect(iframe.getAttribute('sandbox')).toBe(null) + expect(propSetCount).toBe(0) + + patchProp(iframe, 'sandbox', null, '') + expect(iframe.getAttribute('sandbox')).toBe('') + expect(iframe.hasAttribute('sandbox')).toBe(true) + expect(propSetCount).toBe(0) + + delete iframe.sandbox + }) }) diff --git a/packages/runtime-dom/src/patchProp.ts b/packages/runtime-dom/src/patchProp.ts index 27174ddf62..74b5774ec9 100644 --- a/packages/runtime-dom/src/patchProp.ts +++ b/packages/runtime-dom/src/patchProp.ts @@ -111,6 +111,13 @@ function shouldSetAsProp( return false } + // #13946 iframe.sandbox should always be set as attribute since setting + // the property to null results in 'null' string, and setting to empty string + // enables the most restrictive sandbox mode instead of no sandboxing. + if (key === 'sandbox' && el.tagName === 'IFRAME') { + return false + } + // #1787, #2840 form property on form elements is readonly and must be set as // attribute. if (key === 'form') { -- 2.47.3