feat(core): create a Trusted Types policy for bypass conversions (#39218)

When an application uses a custom sanitizer or one of the
bypassSecurityTrust functions, Angular has no way of knowing whether
they are implemented in a secure way. (It doesn't even know if they're
introduced by the application or by a shady third-party dependency.)
Thus using Angular's main Trusted Types policy to bless values coming
from these two sources would undermine the security that Trusted Types
brings.

Instead, introduce a Trusted Types policy called angular#unsafe-bypass
specifically for blessing values from these sources. This allows an
application to enforce Trusted Types even if their application uses a
custom sanitizer or the bypassSecurityTrust functions, knowing that
compromises to either of these two sources may lead to arbitrary script
execution. In the future Angular will provide a way to implement
custom sanitizers in a manner that makes better use of Trusted Types.

PR Close #39218
This commit is contained in:
Bjarki 2020-10-13 17:51:28 +00:00 committed by Andrew Kushnir
parent 9ec2bad4dc
commit 49197d12a0
1 changed files with 89 additions and 0 deletions

View File

@ -0,0 +1,89 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @fileoverview
* A module to facilitate use of a Trusted Types policy internally within
* Angular specifically for bypassSecurityTrust* and custom sanitizers. It
* lazily constructs the Trusted Types policy, providing helper utilities for
* promoting strings to Trusted Types. When Trusted Types are not available,
* strings are used as a fallback.
* @security All use of this module is security-sensitive and should go through
* security review.
*/
import {global} from '../global';
import {TrustedHTML, TrustedScript, TrustedScriptURL, TrustedTypePolicy, TrustedTypePolicyFactory} from './trusted_type_defs';
/**
* The Trusted Types policy, or null if Trusted Types are not
* enabled/supported, or undefined if the policy has not been created yet.
*/
let policy: TrustedTypePolicy|null|undefined;
/**
* Returns the Trusted Types policy, or null if Trusted Types are not
* enabled/supported. The first call to this function will create the policy.
*/
function getPolicy(): TrustedTypePolicy|null {
if (policy === undefined) {
policy = null;
if (global.trustedTypes) {
try {
policy = (global.trustedTypes as TrustedTypePolicyFactory)
.createPolicy('angular#unsafe-bypass', {
createHTML: (s: string) => s,
createScript: (s: string) => s,
createScriptURL: (s: string) => s,
});
} catch {
// trustedTypes.createPolicy throws if called with a name that is
// already registered, even in report-only mode. Until the API changes,
// catch the error not to break the applications functionally. In such
// cases, the code will fall back to using strings.
}
}
}
return policy;
}
/**
* Unsafely promote a string to a TrustedHTML, falling back to strings when
* Trusted Types are not available.
* @security This is a security-sensitive function; any use of this function
* must go through security review. In particular, it must be assured that it
* is only passed strings that come directly from custom sanitizers or the
* bypassSecurityTrust* functions.
*/
export function trustedHTMLFromStringBypass(html: string): TrustedHTML|string {
return getPolicy()?.createHTML(html) || html;
}
/**
* Unsafely promote a string to a TrustedScript, falling back to strings when
* Trusted Types are not available.
* @security This is a security-sensitive function; any use of this function
* must go through security review. In particular, it must be assured that it
* is only passed strings that come directly from custom sanitizers or the
* bypassSecurityTrust* functions.
*/
export function trustedScriptFromStringBypass(script: string): TrustedScript|string {
return getPolicy()?.createScript(script) || script;
}
/**
* Unsafely promote a string to a TrustedScriptURL, falling back to strings
* when Trusted Types are not available.
* @security This is a security-sensitive function; any use of this function
* must go through security review. In particular, it must be assured that it
* is only passed strings that come directly from custom sanitizers or the
* bypassSecurityTrust* functions.
*/
export function trustedScriptURLFromStringBypass(url: string): TrustedScriptURL|string {
return getPolicy()?.createScriptURL(url) || url;
}