feat(compiler): add schema for Trusted Types sinks (#39554)

Create a schema with an associated function to classify Trusted Types
sinks.

Piggyback a typo fix.

PR Close #39554
This commit is contained in:
Bjarki 2020-11-18 07:40:24 +00:00 committed by Andrew Kushnir
parent c7f4abf18a
commit 358c50e226
3 changed files with 82 additions and 1 deletions

View File

@ -19,7 +19,7 @@ import {SecurityContext} from '../core';
//
// =================================================================================================
/** Map from tagName|propertyName SecurityContext. Properties applying to all tags use '*'. */
/** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
let _SECURITY_SCHEMA!: {[k: string]: SecurityContext};
export function SECURITY_SCHEMA(): {[k: string]: SecurityContext} {

View File

@ -0,0 +1,47 @@
/**
* @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
*/
/**
* Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all
* tags use '*'.
*
* Extracted from, and should be kept in sync with
* https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations
*/
const TRUSTED_TYPES_SINKS = new Set<string>([
// NOTE: All strings in this set *must* be lowercase!
// TrustedHTML
'iframe|srcdoc',
'*|innerhtml',
'*|outerhtml',
// NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.
// TrustedScriptURL
'embed|src',
'object|codebase',
'object|data',
]);
/**
* isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types
* sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular
* Trusted Type is required for values passed to the sink:
* - SecurityContext.HTML corresponds to TrustedHTML
* - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL
*/
export function isTrustedTypesSink(tagName: string, propName: string): boolean {
// Make sure comparisons are case insensitive, so that case differences between attribute and
// property names do not have a security impact.
tagName = tagName.toLowerCase();
propName = propName.toLowerCase();
return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) ||
TRUSTED_TYPES_SINKS.has('*|' + propName);
}

View File

@ -0,0 +1,34 @@
/**
* @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
*/
import {isTrustedTypesSink} from '@angular/compiler/src/schema/trusted_types_sinks';
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
{
describe('isTrustedTypesSink', () => {
it('should classify Trusted Types sinks', () => {
expect(isTrustedTypesSink('iframe', 'srcdoc')).toBeTrue();
expect(isTrustedTypesSink('p', 'innerHTML')).toBeTrue();
expect(isTrustedTypesSink('embed', 'src')).toBeTrue();
expect(isTrustedTypesSink('a', 'href')).toBeFalse();
expect(isTrustedTypesSink('base', 'href')).toBeFalse();
expect(isTrustedTypesSink('div', 'style')).toBeFalse();
});
it('should classify Trusted Types sinks case insensitive', () => {
expect(isTrustedTypesSink('p', 'iNnErHtMl')).toBeTrue();
expect(isTrustedTypesSink('p', 'formaction')).toBeFalse();
expect(isTrustedTypesSink('p', 'formAction')).toBeFalse();
});
it('should classify attributes as Trusted Types sinks', () => {
expect(isTrustedTypesSink('p', 'innerHtml')).toBeTrue();
expect(isTrustedTypesSink('p', 'formaction')).toBeFalse();
});
});
}