feat(security): complete DOM security schema.

This addresses several oversights in assigning security contexts to DOM schema
elements found by our security reviewers (thanks!).

This also adds some more precise unit tests for the interaction between
(Dom)ElementSchemaRegistry and the TemplateParser, and extracts the security
specific parts into dom_security_schema.ts.

Comparison of (potentially) dangerous property names is done case insensitive,
to avoid issues like formAction vs formaction.

Part of issue #8511.
This commit is contained in:
Martin Probst
2016-05-26 14:35:27 -07:00
parent a78a43c816
commit 040b101842
4 changed files with 136 additions and 49 deletions

View File

@ -11,6 +11,7 @@ import {
beforeEachProviders
} from '@angular/core/testing/testing_internal';
import {provide} from '@angular/core';
import {SecurityContext} from '../core_private';
import {Console} from '@angular/core/src/console';
@ -51,6 +52,7 @@ import {
import {identifierToken, Identifiers} from '../src/identifiers';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry';
import {MockSchemaRegistry} from '@angular/compiler/testing';
import {Unparser} from './expression_parser/unparser';
@ -66,9 +68,13 @@ var MOCK_SCHEMA_REGISTRY = [
{useValue: new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'})})
];
let zeConsole = console;
export function main() {
var ngIf;
var parse;
var parse:
(template: string, directives: CompileDirectiveMetadata[], pipes?: CompilePipeMetadata[]) =>
TemplateAst[];
var console: ArrayConsole;
function commonBeforeEach() {
@ -120,6 +126,41 @@ export function main() {
});
});
describe('TemplateParser Security', () => {
// Semi-integration test to make sure TemplateParser properly sets the security context.
// Uses the actual DomElementSchemaRegistry.
beforeEachProviders(
() =>
[TEST_PROVIDERS, provide(ElementSchemaRegistry, {useClass: DomElementSchemaRegistry})]);
commonBeforeEach();
describe('security context', () => {
function secContext(tpl: string): SecurityContext {
let ast = parse(tpl, []);
let propBinding = (<ElementAst>ast[0]).inputs[0];
return propBinding.securityContext;
}
it('should set for properties', () => {
expect(secContext('<div [title]="v">')).toBe(SecurityContext.NONE);
expect(secContext('<div [innerHTML]="v">')).toBe(SecurityContext.HTML);
});
it('should set for property value bindings', () => {
expect(secContext('<div innerHTML="{{v}}">')).toBe(SecurityContext.HTML);
});
it('should set for attributes', () => {
expect(secContext('<a [attr.href]="v">')).toBe(SecurityContext.URL);
// NB: attributes below need to change case.
expect(secContext('<a [attr.innerHtml]="v">')).toBe(SecurityContext.HTML);
expect(secContext('<a [attr.formaction]="v">')).toBe(SecurityContext.URL);
});
it('should set for style', () => {
expect(secContext('<a [style.backgroundColor]="v">')).toBe(SecurityContext.STYLE);
});
});
});
describe('TemplateParser', () => {
beforeEachProviders(() => [TEST_PROVIDERS, MOCK_SCHEMA_REGISTRY]);