fix(compiler): disallow i18n of security-sensitive attributes (#39554)
To minimize security risk (XSS in particular) in the i18n pipeline, disallow i18n translation of attributes that are Trusted Types sinks. Add integration tests to ensure that such sinks cannot be translated. PR Close #39554
This commit is contained in:
		
							parent
							
								
									bb70a9bda4
								
							
						
					
					
						commit
						c8a99ef458
					
				| @ -14,6 +14,7 @@ import * as html from '../../../ml_parser/ast'; | |||||||
| import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../ml_parser/interpolation_config'; | import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../ml_parser/interpolation_config'; | ||||||
| import {ParseTreeResult} from '../../../ml_parser/parser'; | import {ParseTreeResult} from '../../../ml_parser/parser'; | ||||||
| import * as o from '../../../output/output_ast'; | import * as o from '../../../output/output_ast'; | ||||||
|  | import {isTrustedTypesSink} from '../../../schema/trusted_types_sinks'; | ||||||
| 
 | 
 | ||||||
| import {hasI18nAttrs, I18N_ATTR, I18N_ATTR_PREFIX, icuFromI18nMessage} from './util'; | import {hasI18nAttrs, I18N_ATTR, I18N_ATTR_PREFIX, icuFromI18nMessage} from './util'; | ||||||
| 
 | 
 | ||||||
| @ -90,9 +91,13 @@ export class I18nMetaVisitor implements html.Visitor { | |||||||
| 
 | 
 | ||||||
|         } else if (attr.name.startsWith(I18N_ATTR_PREFIX)) { |         } else if (attr.name.startsWith(I18N_ATTR_PREFIX)) { | ||||||
|           // 'i18n-*' attributes
 |           // 'i18n-*' attributes
 | ||||||
|           const key = attr.name.slice(I18N_ATTR_PREFIX.length); |           const name = attr.name.slice(I18N_ATTR_PREFIX.length); | ||||||
|           attrsMeta[key] = attr.value; |           if (isTrustedTypesSink(element.name, name)) { | ||||||
| 
 |             this._reportError( | ||||||
|  |                 attr, `Translating attribute '${name}' is disallowed for security reasons.`); | ||||||
|  |           } else { | ||||||
|  |             attrsMeta[name] = attr.value; | ||||||
|  |           } | ||||||
|         } else { |         } else { | ||||||
|           // non-i18n attributes
 |           // non-i18n attributes
 | ||||||
|           attrs.push(attr); |           attrs.push(attr); | ||||||
|  | |||||||
| @ -283,5 +283,31 @@ function declareTests(config?: {useJit: boolean}) { | |||||||
|         expect(e.innerHTML).toEqual('also  evil'); |         expect(e.innerHTML).toEqual('also  evil'); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     onlyInIvy('Trusted Types are only supported in Ivy').describe('translation', () => { | ||||||
|  |       it('should throw error on security-sensitive attributes with constant values', () => { | ||||||
|  |         const template = `<iframe srcdoc="foo" i18n-srcdoc></iframe>`; | ||||||
|  |         TestBed.overrideComponent(SecuredComponent, {set: {template}}); | ||||||
|  | 
 | ||||||
|  |         expect(() => TestBed.createComponent(SecuredComponent)) | ||||||
|  |             .toThrowError(/Translating attribute 'srcdoc' is disallowed for security reasons./); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('should throw error on security-sensitive attributes with interpolated values', () => { | ||||||
|  |         const template = `<object i18n-data data="foo{{bar}}baz"></object>`; | ||||||
|  |         TestBed.overrideComponent(SecuredComponent, {set: {template}}); | ||||||
|  | 
 | ||||||
|  |         expect(() => TestBed.createComponent(SecuredComponent)) | ||||||
|  |             .toThrowError(/Translating attribute 'data' is disallowed for security reasons./); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       it('should throw error on security-sensitive attributes with bound values', () => { | ||||||
|  |         const template = `<div [innerHTML]="foo" i18n-innerHTML></div>`; | ||||||
|  |         TestBed.overrideComponent(SecuredComponent, {set: {template}}); | ||||||
|  | 
 | ||||||
|  |         expect(() => TestBed.createComponent(SecuredComponent)) | ||||||
|  |             .toThrowError(/Translating attribute 'innerHTML' is disallowed for security reasons./); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user