From 49197d12a0a721f6eae3d4cf79581c02955c3b34 Mon Sep 17 00:00:00 2001 From: Bjarki Date: Tue, 13 Oct 2020 17:51:28 +0000 Subject: [PATCH] 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 --- .../src/util/security/trusted_types_bypass.ts | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 packages/core/src/util/security/trusted_types_bypass.ts diff --git a/packages/core/src/util/security/trusted_types_bypass.ts b/packages/core/src/util/security/trusted_types_bypass.ts new file mode 100644 index 0000000000..3280785d08 --- /dev/null +++ b/packages/core/src/util/security/trusted_types_bypass.ts @@ -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; +}