fix(compiler): move detection of unsafe properties for binding to ElementSchemaRegistry (#11378)

This commit is contained in:
Marc Laval
2016-09-28 02:10:02 +02:00
committed by Rado Kirov
parent 3a5b4882bc
commit 61129fa12d
8 changed files with 1512 additions and 1395 deletions

View File

@ -344,4 +344,26 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
getMappedPropName(propName: string): string { return _ATTR_TO_PROP[propName] || propName; }
getDefaultComponentElementName(): string { return 'ng-component'; }
validateProperty(name: string): {error: boolean, msg?: string} {
if (name.toLowerCase().startsWith('on')) {
const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
`please use (${name.slice(2)})=...` +
`\nIf '${name}' is a directive input, make sure the directive is imported by the` +
` current module.`;
return {error: true, msg: msg};
} else {
return {error: false};
}
}
validateAttribute(name: string): {error: boolean, msg?: string} {
if (name.toLowerCase().startsWith('on')) {
const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
`please use (${name.slice(2)})=...`;
return {error: true, msg: msg};
} else {
return {error: false};
}
}
}

View File

@ -14,4 +14,6 @@ export abstract class ElementSchemaRegistry {
abstract securityContext(tagName: string, propName: string): any;
abstract getMappedPropName(propName: string): string;
abstract getDefaultComponentElementName(): string;
abstract validateProperty(name: string): {error: boolean, msg?: string};
abstract validateAttribute(name: string): {error: boolean, msg?: string};
}

View File

@ -927,7 +927,7 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
bindingType = PropertyBindingType.Property;
this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, false);
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
let errorMsg =
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
@ -942,7 +942,7 @@ class TemplateParseVisitor implements html.Visitor {
} else {
if (parts[0] == ATTRIBUTE_PREFIX) {
boundPropertyName = parts[1];
this._assertNoEventBinding(boundPropertyName, sourceSpan, true);
this._validatePropertyOrAttributeName(boundPropertyName, sourceSpan, true);
// NB: For security purposes, use the mapped property name, not the attribute name.
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
@ -975,23 +975,19 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
}
/**
* @param propName the name of the property / attribute
* @param sourceSpan
* @param isAttr true when binding to an attribute
* @private
*/
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean):
void {
if (propName.toLowerCase().startsWith('on')) {
let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` +
`please use (${propName.slice(2)})=...`;
if (!isAttr) {
msg +=
`\nIf '${propName}' is a directive input, make sure the directive is imported by the` +
` current module.`;
}
this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL);
private _validatePropertyOrAttributeName(
propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): void {
const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
this._schemaRegistry.validateProperty(propName);
if (report.error) {
this._reportError(report.msg, sourceSpan, ParseErrorLevel.FATAL);
}
}