diff --git a/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts b/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts index 395c4f7350..1588f273ee 100644 --- a/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts +++ b/modules/@angular/compiler/src/schema/dom_element_schema_registry.ts @@ -206,6 +206,53 @@ var attrToPropMap: {[name: string]: string} = { 'tabindex': 'tabIndex' }; +function registerContext(map: {[k: string]: SecurityContext}, ctx: SecurityContext, specs: string[]) { + for (let spec of specs) map[spec] = ctx; +} + +/** Map from tagName|propertyName SecurityContext. Properties applying to all tags use '*'. */ +const SECURITY_SCHEMA: {[k: string]: SecurityContext} = {}; + +registerContext(SECURITY_SCHEMA, SecurityContext.HTML, [ + 'iframe|srcdoc', + '*|innerHTML', + '*|outerHTML', +]); +registerContext(SECURITY_SCHEMA, SecurityContext.STYLE, ['*|style']); +// NB: no SCRIPT contexts here, they are never allowed. +registerContext(SECURITY_SCHEMA, SecurityContext.URL, [ + 'area|href', + 'area|ping', + 'audio|src', + 'a|href', + 'a|ping', + 'blockquote|cite', + 'body|background', + 'button|formaction', + 'del|cite', + 'form|action', + 'img|src', + 'input|formaction', + 'input|src', + 'ins|cite', + 'q|cite', + 'source|src', + 'video|poster', + 'video|src', +]); +registerContext(SECURITY_SCHEMA, SecurityContext.RESOURCE_URL, [ + 'applet|code', + 'applet|codebase', + 'base|href', + 'frame|src', + 'head|profile', + 'html|manifest', + 'iframe|src', + 'object|codebase', + 'object|data', + 'script|src', + 'track|src', +]); @Injectable() export class DomElementSchemaRegistry extends ElementSchemaRegistry { @@ -267,11 +314,10 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry { * attack vectors are assigned their appropriate context. */ securityContext(tagName: string, propName: string): SecurityContext { - // TODO(martinprobst): Fill in missing properties. - if (propName === 'style') return SecurityContext.STYLE; - if (tagName === 'a' && propName === 'href') return SecurityContext.URL; - if (propName === 'innerHTML') return SecurityContext.HTML; - return SecurityContext.NONE; + let ctx = SECURITY_SCHEMA[tagName + '|' + propName]; + if (ctx !== undefined) return ctx; + ctx = SECURITY_SCHEMA['*|' + propName]; + return ctx !== undefined ? ctx : SecurityContext.NONE; } getMappedPropName(propName: string): string { diff --git a/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts b/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts index d5f1cd9367..11ce131d32 100644 --- a/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts +++ b/modules/@angular/compiler/test/schema/dom_element_schema_registry_spec.ts @@ -57,8 +57,14 @@ export function main() { expect(registry.getMappedPropName('exotic-unknown')).toEqual('exotic-unknown'); }); - it('should return security contexts for elements', - () => { expect(registry.securityContext('a', 'href')).toBe(SecurityContext.URL); }); + it('should return security contexts for elements', () => { + expect(registry.securityContext('iframe', 'srcdoc')).toBe(SecurityContext.HTML); + expect(registry.securityContext('p', 'innerHTML')).toBe(SecurityContext.HTML); + expect(registry.securityContext('a', 'href')).toBe(SecurityContext.URL); + expect(registry.securityContext('a', 'style')).toBe(SecurityContext.STYLE); + expect(registry.securityContext('ins', 'cite')).toBe(SecurityContext.URL); + expect(registry.securityContext('base', 'href')).toBe(SecurityContext.RESOURCE_URL); + }); it('should detect properties on namespaced elements', () => { expect(registry.hasProperty('@svg:g', 'id')).toBeTruthy(); });