fix(core): convert legacy-sanitized values to Trusted Types (#39218)

Use the bypass-specific Trusted Types policy for automatically upgrade
any values from custom sanitizers or the bypassSecurityTrust functions
to a Trusted Type. Update tests to reflect the new behavior.

PR Close #39218
This commit is contained in:
Bjarki 2020-10-10 02:16:12 +00:00 committed by Andrew Kushnir
parent 49197d12a0
commit 81aa119739
2 changed files with 14 additions and 10 deletions

View File

@ -12,6 +12,7 @@ import {getLView} from '../render3/state';
import {renderStringify} from '../render3/util/misc_utils'; import {renderStringify} from '../render3/util/misc_utils';
import {TrustedHTML, TrustedScript, TrustedScriptURL} from '../util/security/trusted_type_defs'; import {TrustedHTML, TrustedScript, TrustedScriptURL} from '../util/security/trusted_type_defs';
import {trustedHTMLFromString, trustedScriptFromString, trustedScriptURLFromString} from '../util/security/trusted_types'; import {trustedHTMLFromString, trustedScriptFromString, trustedScriptURLFromString} from '../util/security/trusted_types';
import {trustedHTMLFromStringBypass, trustedScriptFromStringBypass, trustedScriptURLFromStringBypass} from '../util/security/trusted_types_bypass';
import {allowSanitizationBypassAndThrow, BypassType, unwrapSafeValue} from './bypass'; import {allowSanitizationBypassAndThrow, BypassType, unwrapSafeValue} from './bypass';
import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer'; import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer';
@ -39,10 +40,10 @@ import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer';
export function ɵɵsanitizeHtml(unsafeHtml: any): TrustedHTML|string { export function ɵɵsanitizeHtml(unsafeHtml: any): TrustedHTML|string {
const sanitizer = getSanitizer(); const sanitizer = getSanitizer();
if (sanitizer) { if (sanitizer) {
return sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || ''; return trustedHTMLFromStringBypass(sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || '');
} }
if (allowSanitizationBypassAndThrow(unsafeHtml, BypassType.Html)) { if (allowSanitizationBypassAndThrow(unsafeHtml, BypassType.Html)) {
return unwrapSafeValue(unsafeHtml); return trustedHTMLFromStringBypass(unwrapSafeValue(unsafeHtml));
} }
return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml)); return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml));
} }
@ -110,10 +111,11 @@ export function ɵɵsanitizeUrl(unsafeUrl: any): string {
export function ɵɵsanitizeResourceUrl(unsafeResourceUrl: any): TrustedScriptURL|string { export function ɵɵsanitizeResourceUrl(unsafeResourceUrl: any): TrustedScriptURL|string {
const sanitizer = getSanitizer(); const sanitizer = getSanitizer();
if (sanitizer) { if (sanitizer) {
return sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || ''; return trustedScriptURLFromStringBypass(
sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || '');
} }
if (allowSanitizationBypassAndThrow(unsafeResourceUrl, BypassType.ResourceUrl)) { if (allowSanitizationBypassAndThrow(unsafeResourceUrl, BypassType.ResourceUrl)) {
return unwrapSafeValue(unsafeResourceUrl); return trustedScriptURLFromStringBypass(unwrapSafeValue(unsafeResourceUrl));
} }
throw new Error('unsafe value used in a resource URL context (see http://g.co/ng/security#xss)'); throw new Error('unsafe value used in a resource URL context (see http://g.co/ng/security#xss)');
} }
@ -133,10 +135,11 @@ export function ɵɵsanitizeResourceUrl(unsafeResourceUrl: any): TrustedScriptUR
export function ɵɵsanitizeScript(unsafeScript: any): TrustedScript|string { export function ɵɵsanitizeScript(unsafeScript: any): TrustedScript|string {
const sanitizer = getSanitizer(); const sanitizer = getSanitizer();
if (sanitizer) { if (sanitizer) {
return sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || ''; return trustedScriptFromStringBypass(
sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || '');
} }
if (allowSanitizationBypassAndThrow(unsafeScript, BypassType.Script)) { if (allowSanitizationBypassAndThrow(unsafeScript, BypassType.Script)) {
return unwrapSafeValue(unsafeScript); return trustedScriptFromStringBypass(unwrapSafeValue(unsafeScript));
} }
throw new Error('unsafe value used in a script context'); throw new Error('unsafe value used in a script context');
} }

View File

@ -37,7 +37,7 @@ describe('sanitization', () => {
.toEqual('<img src="unsafe:javascript:true">'); .toEqual('<img src="unsafe:javascript:true">');
expect(() => ɵɵsanitizeHtml(bypassSanitizationTrustUrl('<img src="javascript:true">'))) expect(() => ɵɵsanitizeHtml(bypassSanitizationTrustUrl('<img src="javascript:true">')))
.toThrowError(/Required a safe HTML, got a URL/); .toThrowError(/Required a safe HTML, got a URL/);
expect(ɵɵsanitizeHtml(bypassSanitizationTrustHtml('<img src="javascript:true">'))) expect(ɵɵsanitizeHtml(bypassSanitizationTrustHtml('<img src="javascript:true">')).toString())
.toEqual('<img src="javascript:true">'); .toEqual('<img src="javascript:true">');
}); });
@ -57,7 +57,7 @@ describe('sanitization', () => {
expect(() => ɵɵsanitizeResourceUrl('javascript:true')).toThrowError(ERROR); expect(() => ɵɵsanitizeResourceUrl('javascript:true')).toThrowError(ERROR);
expect(() => ɵɵsanitizeResourceUrl(bypassSanitizationTrustHtml('javascript:true'))) expect(() => ɵɵsanitizeResourceUrl(bypassSanitizationTrustHtml('javascript:true')))
.toThrowError(/Required a safe ResourceURL, got a HTML/); .toThrowError(/Required a safe ResourceURL, got a HTML/);
expect(ɵɵsanitizeResourceUrl(bypassSanitizationTrustResourceUrl('javascript:true'))) expect(ɵɵsanitizeResourceUrl(bypassSanitizationTrustResourceUrl('javascript:true')).toString())
.toEqual('javascript:true'); .toEqual('javascript:true');
}); });
@ -78,7 +78,7 @@ describe('sanitization', () => {
expect(() => ɵɵsanitizeScript('true')).toThrowError(ERROR); expect(() => ɵɵsanitizeScript('true')).toThrowError(ERROR);
expect(() => ɵɵsanitizeScript(bypassSanitizationTrustHtml('true'))) expect(() => ɵɵsanitizeScript(bypassSanitizationTrustHtml('true')))
.toThrowError(/Required a safe Script, got a HTML/); .toThrowError(/Required a safe Script, got a HTML/);
expect(ɵɵsanitizeScript(bypassSanitizationTrustScript('true'))).toEqual('true'); expect(ɵɵsanitizeScript(bypassSanitizationTrustScript('true')).toString()).toEqual('true');
}); });
it('should select correct sanitizer for URL props', () => { it('should select correct sanitizer for URL props', () => {
@ -114,7 +114,8 @@ describe('sanitization', () => {
bypassSanitizationTrustHtml('javascript:true'), 'iframe', 'src')) bypassSanitizationTrustHtml('javascript:true'), 'iframe', 'src'))
.toThrowError(/Required a safe ResourceURL, got a HTML/); .toThrowError(/Required a safe ResourceURL, got a HTML/);
expect(ɵɵsanitizeUrlOrResourceUrl( expect(ɵɵsanitizeUrlOrResourceUrl(
bypassSanitizationTrustResourceUrl('javascript:true'), 'iframe', 'src')) bypassSanitizationTrustResourceUrl('javascript:true'), 'iframe', 'src')
.toString())
.toEqual('javascript:true'); .toEqual('javascript:true');
}); });