@@ -13,6 +13,7 @@ import {CommonModule, DOCUMENT, registerLocaleData} from '@angular/common';
1313import localeEs from '@angular/common/locales/es' ;
1414import localeRo from '@angular/common/locales/ro' ;
1515import { computeMsgId } from '@angular/compiler' ;
16+ import { isBrowser } from '@angular/private/testing' ;
1617import {
1718 Attribute ,
1819 Component ,
@@ -3506,7 +3507,7 @@ describe('runtime i18n', () => {
35063507 <div i18n>{
35073508 parameters.length,
35083509 plural,
3509- =1 {Affects parameter <span class="parameter-name" attr ="should_be_present">{{parameters[0].name}}</span>}
3510+ =1 {Affects parameter <span class="parameter-name" label ="should_be_present">{{parameters[0].name}}</span>}
35103511 other {Affects {{parameters.length}} parameters, including <span
35113512 class="parameter-name">{{parameters[0].name}}</span>}
35123513 }</div>
@@ -3521,7 +3522,7 @@ describe('runtime i18n', () => {
35213522 const fixture = TestBed . createComponent ( MyApp ) ;
35223523 fixture . detectChanges ( ) ;
35233524 const span = ( fixture . nativeElement as HTMLElement ) . querySelector ( 'span' ) ! ;
3524- expect ( span . getAttribute ( 'attr ' ) ) . toEqual ( 'should_be_present' ) ;
3525+ expect ( span . getAttribute ( 'label ' ) ) . toEqual ( 'should_be_present' ) ;
35253526 expect ( span . getAttribute ( 'class' ) ) . toEqual ( 'parameter-name' ) ;
35263527 } ) ;
35273528
@@ -3607,6 +3608,92 @@ describe('runtime i18n', () => {
36073608 'translatedText value' ,
36083609 ) ;
36093610 } ) ;
3611+
3612+ describe ( 'attribute sanitization' , ( ) => {
3613+ @Component ( { template : '' } )
3614+ class SanitizeAppComp {
3615+ url = 'javascript:alert("oh no")' ;
3616+ count = 0 ;
3617+ }
3618+
3619+ it ( 'should sanitize translated attribute binding' , ( ) => {
3620+ const fixture = initWithTemplate ( SanitizeAppComp , '<a [attr.href]="url" i18n-href></a>' ) ;
3621+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3622+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3623+ } ) ;
3624+
3625+ it ( 'should sanitize translated property binding' , ( ) => {
3626+ const fixture = initWithTemplate ( SanitizeAppComp , '<a [href]="url" i18n-href></a>' ) ;
3627+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3628+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3629+ } ) ;
3630+
3631+ it ( 'should sanitize translated interpolation' , ( ) => {
3632+ const fixture = initWithTemplate ( SanitizeAppComp , '<a href="{{url}}" i18n-href></a>' ) ;
3633+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3634+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3635+ } ) ;
3636+
3637+ it ( 'should sanitize interpolation inside translated element' , ( ) => {
3638+ const fixture = initWithTemplate ( SanitizeAppComp , `<div i18n><a href="{{url}}"></a></div>` ) ;
3639+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3640+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3641+ } ) ;
3642+
3643+ it ( 'should sanitize attribute binding inside translated element' , ( ) => {
3644+ const fixture = initWithTemplate (
3645+ SanitizeAppComp ,
3646+ `<div i18n><a [attr.href]="url"></a></div>` ,
3647+ ) ;
3648+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3649+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3650+ } ) ;
3651+
3652+ it ( 'should sanitize property binding inside translated element' , ( ) => {
3653+ const fixture = initWithTemplate ( SanitizeAppComp , `<div i18n><a [href]="url"></a></div>` ) ;
3654+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3655+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3656+ } ) ;
3657+
3658+ it ( 'should sanitize property binding inside an ICU' , ( ) => {
3659+ const fixture = initWithTemplate (
3660+ SanitizeAppComp ,
3661+ `<div i18n>{count, plural,
3662+ =0 {no <strong>link</strong> yet}
3663+ other {{{count}} Here is the <a href="{{url}}">link</a>!}
3664+ }</div>` ,
3665+ ) ;
3666+
3667+ expect ( fixture . nativeElement . querySelector ( 'a' ) ) . toBeFalsy ( ) ;
3668+
3669+ fixture . componentInstance . count = 1 ;
3670+ fixture . detectChanges ( ) ;
3671+ const link : HTMLAnchorElement = fixture . nativeElement . querySelector ( 'a' ) ;
3672+ expect ( link ) . toBeTruthy ( ) ;
3673+ expect ( link . getAttribute ( 'href' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3674+ } ) ;
3675+
3676+ it ( 'should sanitize action binding' , ( ) => {
3677+ const fixture = initWithTemplate (
3678+ SanitizeAppComp ,
3679+ '<form action="{{url}}" i18n-action></form>' ,
3680+ ) ;
3681+ const form : HTMLFormElement = fixture . nativeElement . querySelector ( 'form' ) ;
3682+ expect ( form . getAttribute ( 'action' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3683+ } ) ;
3684+
3685+ // Skip this test in Node, because Domino doesn't support `formAction`.
3686+ if ( isBrowser ) {
3687+ it ( 'should sanitize formaction binding' , ( ) => {
3688+ const fixture = initWithTemplate (
3689+ SanitizeAppComp ,
3690+ '<input type="text" formaction="{{url}}" i18n-formaction>' ,
3691+ ) ;
3692+ const input : HTMLInputElement = fixture . nativeElement . querySelector ( 'input' ) ;
3693+ expect ( input . getAttribute ( 'formaction' ) ) . toMatch ( / ^ u n s a f e : / ) ;
3694+ } ) ;
3695+ }
3696+ } ) ;
36103697} ) ;
36113698
36123699function initWithTemplate ( compType : Type < any > , template : string ) {
0 commit comments