diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 1562ed407f..4f6a1b83dc 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -755,7 +755,9 @@ export function elementProperty( setInputsForProperty(dataValue, value); markDirtyIfOnPush(node); } else { - value = (sanitizer != null ? sanitizer(value) : stringify(value)) as any; + // It is assumed that the sanitizer is only added when the compiler determines that the property + // is risky, so sanitization can be done without further checks. + value = sanitizer != null ? (sanitizer(value) as any) : value; const native = node.native; isProceduralRenderer(renderer) ? renderer.setProperty(native, propName, value) : (native.setProperty ? native.setProperty(propName, value) : diff --git a/packages/core/test/render3/compiler_canonical/sanitize_spec.ts b/packages/core/test/render3/compiler_canonical/sanitize_spec.ts index bbfa4cfb44..fb2b82c1c2 100644 --- a/packages/core/test/render3/compiler_canonical/sanitize_spec.ts +++ b/packages/core/test/render3/compiler_canonical/sanitize_spec.ts @@ -25,12 +25,13 @@ describe('compiler sanitization', () => { @Component({ selector: 'my-component', - template: `
` + + template: `
` + `` + `` }) class MyComponent { innerHTML: string = ''; + hidden: boolean = true; style: string = `url("http://evil")`; url: string = 'javascript:evil()'; @@ -47,6 +48,7 @@ describe('compiler sanitization', () => { $r3$.ɵe(); } $r3$.ɵp(0, 'innerHTML', $r3$.ɵb(ctx.innerHTML), $r3$.ɵsanitizeHtml); + $r3$.ɵp(0, 'hidden', $r3$.ɵb(ctx.hidden)); $r3$.ɵs(1, 'background-image', $r3$.ɵb(ctx.style), $r3$.ɵsanitizeStyle); $r3$.ɵp(1, 'src', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl); $r3$.ɵa(1, 'srcset', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl); @@ -59,6 +61,7 @@ describe('compiler sanitization', () => { const div = getHostElement(myComponent).querySelector('div') !; // because sanitizer removed it is working. expect(div.innerHTML).toEqual(''); + expect(div.hidden).toEqual(true); const img = getHostElement(myComponent).querySelector('img') !; // because sanitizer removed it is working. diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index 9240f5a14d..d2e616a56f 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -34,7 +34,7 @@ describe('instructions', () => { }); describe('elementProperty', () => { - it('should use sanitizer function', () => { + it('should use sanitizer function when available', () => { const t = new TemplateFixture(createDiv); t.update(() => elementProperty(0, 'title', 'javascript:true', sanitizeUrl)); @@ -45,6 +45,14 @@ describe('instructions', () => { 0, 'title', bypassSanitizationTrustUrl('javascript:false'), sanitizeUrl)); expect(t.html).toEqual('
'); }); + + it('should not stringify non string values', () => { + const t = new TemplateFixture(createDiv); + + t.update(() => elementProperty(0, 'hidden', false)); + // The hidden property would be true if `false` was stringified into `"false"`. + expect((t.hostNode.native as HTMLElement).querySelector('div') !.hidden).toEqual(false); + }); }); describe('elementStyle', () => {