Skip to content

Commit

Permalink
fix(core): ensure sanitizer works if DOMParser return null body (#40107)
Browse files Browse the repository at this point in the history
In some browsers, notably a mobile version of webkit on iPad, the
result of calling `DOMParser.parseFromString()` returns a document
whose `body` property is null until the next tick of the browser.
Since this is of no use to us for sanitization, we now fall back to the
"inert document" strategy for this case.

Fixes #39834

PR Close #40107
  • Loading branch information
petebacondarwin authored and josephperrott committed Jan 6, 2021
1 parent 738b8f1 commit add7cbb
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 1 deletion.
11 changes: 10 additions & 1 deletion packages/core/src/sanitization/inert_body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {trustedHTMLFromString} from '../util/security/trusted_types';
* Fallback: InertDocument strategy
*/
export function getInertBodyHelper(defaultDoc: Document): InertBodyHelper {
return isDOMParserAvailable() ? new DOMParserHelper() : new InertDocumentHelper(defaultDoc);
const inertDocumentHelper = new InertDocumentHelper(defaultDoc);
return isDOMParserAvailable() ? new DOMParserHelper(inertDocumentHelper) : inertDocumentHelper;
}

export interface InertBodyHelper {
Expand All @@ -31,6 +32,8 @@ export interface InertBodyHelper {
* This is the default strategy used in browsers that support it.
*/
class DOMParserHelper implements InertBodyHelper {
constructor(private inertDocumentHelper: InertBodyHelper) {}

getInertBodyElement(html: string): HTMLElement|null {
// 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 `<meta>` do not get hoisted to the
Expand All @@ -41,6 +44,12 @@ class DOMParserHelper implements InertBodyHelper {
const body = new window.DOMParser()
.parseFromString(trustedHTMLFromString(html) as string, 'text/html')
.body as HTMLBodyElement;
if (body === null) {
// In some browsers (e.g. Mozilla/5.0 iPad AppleWebKit Mobile) the `body` property only
// becomes available in the following tick of the JS engine. In that case we fall back to
// the `inertDocumentHelper` instead.
return this.inertDocumentHelper.getInertBodyElement(html);
}
body.removeChild(body.firstChild!);
return body;
} catch {
Expand Down
9 changes: 9 additions & 0 deletions packages/core/test/sanitization/html_sanitizer_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,14 @@ import {isDOMParserAvailable} from '../../src/sanitization/inert_body';
.toMatch(/<a href="unsafe:(&#12288;)?javascript:alert\(1\)">CLICKME<\/a>/);
});
}

if (isDOMParserAvailable()) {
it('should work even if DOMParser returns a null body', () => {
// Simulate `DOMParser.parseFromString()` returning a null body.
// See https://github.com/angular/angular/issues/39834
spyOn(window.DOMParser.prototype, 'parseFromString').and.returnValue({body: null} as any);
expect(sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World');
});
}
});
}

0 comments on commit add7cbb

Please sign in to comment.