diff --git a/packages/runtime-dom/__tests__/patchAttrs.spec.ts b/packages/runtime-dom/__tests__/patchAttrs.spec.ts index 393b685b0e9..d181a1b038b 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 27174ddf624..74b5774ec9e 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') {