From 9ec2bad4dc2b965fca5ae0f4051ed250dc616f65 Mon Sep 17 00:00:00 2001
From: Bjarki
Date: Sat, 10 Oct 2020 00:27:29 +0000
Subject: [PATCH] refactor(core): make HTML sanitizer return TrustedHTML
(#39218)
Make Angular's HTML sanitizer return a TrustedHTML, as its output is
trusted not to cause XSS vulnerabilities when used in a context where a
browser may parse and evaluate HTML. Also update tests to reflect the
new behaviour.
PR Close #39218
---
.../core/src/sanitization/html_sanitizer.ts | 6 +-
.../test/sanitization/html_sanitizer_spec.ts | 91 ++++++++++---------
.../test/sanitization/sanitization_spec.ts | 8 +-
.../src/security/dom_sanitization_service.ts | 2 +-
4 files changed, 56 insertions(+), 51 deletions(-)
diff --git a/packages/core/src/sanitization/html_sanitizer.ts b/packages/core/src/sanitization/html_sanitizer.ts
index ab7bbbe836..b0b30311a0 100644
--- a/packages/core/src/sanitization/html_sanitizer.ts
+++ b/packages/core/src/sanitization/html_sanitizer.ts
@@ -7,6 +7,8 @@
*/
import {isDevMode} from '../util/is_dev_mode';
+import {TrustedHTML} from '../util/security/trusted_type_defs';
+import {trustedHTMLFromString} from '../util/security/trusted_types';
import {getInertBodyHelper, InertBodyHelper} from './inert_body';
import {_sanitizeUrl, sanitizeSrcset} from './url_sanitizer';
@@ -242,7 +244,7 @@ let inertBodyHelper: InertBodyHelper;
* Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to
* the DOM in a browser environment.
*/
-export function _sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string {
+export function _sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): TrustedHTML|string {
let inertBodyElement: HTMLElement|null = null;
try {
inertBodyHelper = inertBodyHelper || getInertBodyHelper(defaultDoc);
@@ -274,7 +276,7 @@ export function _sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string
'WARNING: sanitizing HTML stripped some content, see http://g.co/ng/security#xss');
}
- return safeHtml;
+ return trustedHTMLFromString(safeHtml);
} finally {
// In case anything goes wrong, clear out inertElement to reset the entire DOM structure.
if (inertBodyElement) {
diff --git a/packages/core/test/sanitization/html_sanitizer_spec.ts b/packages/core/test/sanitization/html_sanitizer_spec.ts
index d577ce2c4d..aae4a18421 100644
--- a/packages/core/test/sanitization/html_sanitizer_spec.ts
+++ b/packages/core/test/sanitization/html_sanitizer_spec.ts
@@ -11,6 +11,10 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut
import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer';
import {isDOMParserAvailable} from '../../src/sanitization/inert_body';
+function sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string {
+ return _sanitizeHtml(defaultDoc, unsafeHtmlInput).toString();
+}
+
{
describe('HTML sanitizer', () => {
let defaultDoc: any;
@@ -29,73 +33,73 @@ import {isDOMParserAvailable} from '../../src/sanitization/inert_body';
});
it('serializes nested structures', () => {
- expect(_sanitizeHtml(defaultDoc, '
bar')).toEqual('foobar');
- expect(_sanitizeHtml(defaultDoc, 'foobar')).toEqual('foobar');
+ expect(sanitizeHtml(defaultDoc, 'bar')).toEqual('foobar');
+ expect(sanitizeHtml(defaultDoc, 'foobar')).toEqual('foobar');
});
it('should not enter an infinite loop on clobbered elements', () => {
@@ -200,18 +204,17 @@ import {isDOMParserAvailable} from '../../src/sanitization/inert_body';
// Anyway what we want to test is that browsers do not enter an infinite loop which would
// result in a timeout error for the test.
try {
- _sanitizeHtml(defaultDoc, '');
+ sanitizeHtml(defaultDoc, '');
} catch (e) {
// depending on the browser, we might ge an exception
}
try {
- _sanitizeHtml(defaultDoc, '');
+ sanitizeHtml(defaultDoc, '');
} catch (e) {
// depending on the browser, we might ge an exception
}
try {
- _sanitizeHtml(
- defaultDoc, '');
+ sanitizeHtml(defaultDoc, '');
} catch (e) {
// depending on the browser, we might ge an exception
}
@@ -220,7 +223,7 @@ import {isDOMParserAvailable} from '../../src/sanitization/inert_body';
// See
// https://github.com/cure53/DOMPurify/blob/a992d3a75031cb8bb032e5ea8399ba972bdf9a65/src/purify.js#L439-L449
it('should not allow JavaScript execution when creating inert document', () => {
- const output = _sanitizeHtml(defaultDoc, '');
+ const output = sanitizeHtml(defaultDoc, '');
const window = defaultDoc.defaultView;
if (window) {
expect(window.xxx).toBe(undefined);
@@ -232,7 +235,7 @@ import {isDOMParserAvailable} from '../../src/sanitization/inert_body';
// See https://github.com/cure53/DOMPurify/releases/tag/0.6.7
it('should not allow JavaScript hidden in badly formed HTML to get through sanitization (Firefox bug)',
() => {
- expect(_sanitizeHtml(
+ expect(sanitizeHtml(
defaultDoc, '