From b950d4675fb76611ee1b9af4eb35db15c46aacf0 Mon Sep 17 00:00:00 2001 From: Harri Lehtola Date: Sat, 11 Apr 2020 16:29:47 +0300 Subject: [PATCH] fix(core): do not trigger CSP alert/report in Firefox and Chrome (#36578) (#36578) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If [innerHTML] is used in a component and a Content-Security-Policy is set that does not allow inline styles then Firefox and Chrome show the following message: > Content Security Policy: The page’s settings observed the loading of a resource at self (“default-src”). A CSP report is being sent. This message is caused because Angular is creating an inline style tag to test for a browser bug that we use to decide what sanitization strategy to use, which causes CSP violation errors if inline CSS is prohibited. This test is no longer necessary, since the `DOMParser` is now safe to use and the `style` based check is redundant. In this fix, we default to using `DOMParser` if it is available and fall back to `createHTMLDocument()` if needed. This is the approach used by DOMPurify too. The related unit tests in `html_sanitizer_spec.ts`, "should not allow JavaScript execution when creating inert document" and "should not allow JavaScript hidden in badly formed HTML to get through sanitization (Firefox bug)", are left untouched to assert that the behavior hasn't changed in those scenarios. Fixes #25214. PR Close #36578 --- packages/core/src/sanitization/inert_body.ts | 68 +++----------------- 1 file changed, 9 insertions(+), 59 deletions(-) diff --git a/packages/core/src/sanitization/inert_body.ts b/packages/core/src/sanitization/inert_body.ts index 46f76eb8f4..e859f8af5a 100644 --- a/packages/core/src/sanitization/inert_body.ts +++ b/packages/core/src/sanitization/inert_body.ts @@ -9,49 +9,26 @@ /** * This helper class is used to get hold of an inert tree of DOM elements containing dirty HTML * that needs sanitizing. - * Depending upon browser support we must use one of three strategies for doing this. - * Support: Safari 10.x -> XHR strategy - * Support: Firefox -> DomParser strategy - * Default: InertDocument strategy + * Depending upon browser support we use one of two strategies for doing this. + * Default: DomParser strategy + * Fallback: InertDocument strategy */ export class InertBodyHelper { private inertDocument: Document; constructor(private defaultDoc: Document) { this.inertDocument = this.defaultDoc.implementation.createHTMLDocument('sanitization-inert'); - let inertBodyElement = this.inertDocument.body; - - if (inertBodyElement == null) { + if (this.inertDocument.body == null) { // usually there should be only one body element in the document, but IE doesn't have any, so // we need to create one. const inertHtml = this.inertDocument.createElement('html'); this.inertDocument.appendChild(inertHtml); - inertBodyElement = this.inertDocument.createElement('body'); + const inertBodyElement = this.inertDocument.createElement('body'); inertHtml.appendChild(inertBodyElement); } - inertBodyElement.innerHTML = ''; - if (inertBodyElement.querySelector && !inertBodyElement.querySelector('svg')) { - // We just hit the Safari 10.1 bug - which allows JS to run inside the SVG G element - // so use the XHR strategy. - this.getInertBodyElement = this.getInertBodyElement_XHR; - return; - } - - inertBodyElement.innerHTML = '

'; - if (inertBodyElement.querySelector && inertBodyElement.querySelector('svg img')) { - // We just hit the Firefox bug - which prevents the inner img JS from being sanitized - // so use the DOMParser strategy, if it is available. - // If the DOMParser is not available then we are not in Firefox (Server/WebWorker?) so we - // fall through to the default strategy below. - if (isDOMParserAvailable()) { - this.getInertBodyElement = this.getInertBodyElement_DOMParser; - return; - } - } - - // None of the bugs were hit so it is safe for us to use the default InertDocument strategy - this.getInertBodyElement = this.getInertBodyElement_InertDocument; + this.getInertBodyElement = isDOMParserAvailable() ? this.getInertBodyElement_DOMParser : + this.getInertBodyElement_InertDocument; } /** @@ -61,33 +38,7 @@ export class InertBodyHelper { getInertBodyElement: (html: string) => HTMLElement | null; /** - * Use XHR to create and fill an inert body element (on Safari 10.1) - * See - * https://github.com/cure53/DOMPurify/blob/a992d3a75031cb8bb032e5ea8399ba972bdf9a65/src/purify.js#L439-L449 - */ - private getInertBodyElement_XHR(html: string) { - // We add these extra elements to ensure that the rest of the content is parsed as expected - // e.g. leading whitespace is maintained and tags like `` do not get hoisted to the - // `` tag. - html = '' + html + ''; - try { - html = encodeURI(html); - } catch { - return null; - } - const xhr = new XMLHttpRequest(); - xhr.responseType = 'document'; - xhr.open('GET', 'data:text/html;charset=utf-8,' + html, false); - xhr.send(undefined); - const body: HTMLBodyElement = xhr.response.body; - body.removeChild(body.firstChild!); - return body; - } - - /** - * Use DOMParser to create and fill an inert body element (on Firefox) - * See https://github.com/cure53/DOMPurify/releases/tag/0.6.7 - * + * Use DOMParser to create and fill an inert body element in browsers that support it. */ private getInertBodyElement_DOMParser(html: string) { // We add these extra elements to ensure that the rest of the content is parsed as expected @@ -107,8 +58,7 @@ export class InertBodyHelper { /** * Use an HTML5 `template` element, if supported, or an inert body element created via * `createHtmlDocument` to create and fill an inert DOM element. - * This is the default sane strategy to use if the browser does not require one of the specialised - * strategies above. + * This is the fallback strategy if the browser does not support DOMParser. */ private getInertBodyElement_InertDocument(html: string) { // Prefer using