1+ /**
2+ * Copyright (c) Facebook, Inc. and its affiliates.
3+ *
4+ * This source code is licensed under the MIT license found in the
5+ * LICENSE file in the root directory of this source tree.
6+ *
7+ * @emails react-core
8+ */
9+
10+ 'use strict' ;
11+
112describe ( 'when Trusted Types are available in global object' , ( ) => {
213 let React ;
314 let ReactDOM ;
415 let ReactFeatureFlags ;
516 let container ;
17+ let ttObject1 ;
18+ let ttObject2 ;
619
720 beforeEach ( ( ) => {
21+ jest . resetModules ( ) ;
822 container = document . createElement ( 'div' ) ;
23+ const fakeTTObjects = new Set ( ) ;
924 window . trustedTypes = {
10- isHTML : ( ) => true ,
25+ isHTML : value => fakeTTObjects . has ( value ) ,
1126 isScript : ( ) => false ,
1227 isScriptURL : ( ) => false ,
1328 } ;
1429 ReactFeatureFlags = require ( 'shared/ReactFeatureFlags' ) ;
1530 ReactFeatureFlags . enableTrustedTypesIntegration = true ;
1631 React = require ( 'react' ) ;
1732 ReactDOM = require ( 'react-dom' ) ;
33+ ttObject1 = {
34+ toString ( ) {
35+ return '<b>Hi</b>' ;
36+ } ,
37+ } ;
38+ ttObject2 = {
39+ toString ( ) {
40+ return '<b>Bye</b>' ;
41+ } ,
42+ } ;
43+ fakeTTObjects . add ( ttObject1 ) ;
44+ fakeTTObjects . add ( ttObject2 ) ;
1845 } ) ;
1946
2047 afterEach ( ( ) => {
2148 delete window . trustedTypes ;
22- ReactFeatureFlags . enableTrustedTypesIntegration = false ;
2349 } ) ;
2450
25- it ( 'should not stringify trusted values' , ( ) => {
26- const trustedObject = { toString : ( ) => 'I look like a trusted object' } ;
27- class Component extends React . Component {
28- state = { inner : undefined } ;
29- render ( ) {
30- return < div dangerouslySetInnerHTML = { { __html : this . state . inner } } /> ;
31- }
51+ it ( 'should not stringify trusted values for dangerouslySetInnerHTML' , ( ) => {
52+ let innerHTMLDescriptor = Object . getOwnPropertyDescriptor (
53+ Element . prototype ,
54+ 'innerHTML' ,
55+ ) ;
56+ try {
57+ const innerHTMLCalls = [ ] ;
58+ Object . defineProperty ( Element . prototype , 'innerHTML' , {
59+ get ( ) {
60+ return innerHTMLDescriptor . get . apply ( this , arguments ) ;
61+ } ,
62+ set ( value ) {
63+ innerHTMLCalls . push ( value ) ;
64+ return innerHTMLDescriptor . set . apply ( this , arguments ) ;
65+ } ,
66+ } ) ;
67+ ReactDOM . render (
68+ < div dangerouslySetInnerHTML = { { __html : ttObject1 } } /> ,
69+ container ,
70+ ) ;
71+ expect ( container . innerHTML ) . toBe ( '<div><b>Hi</b></div>' ) ;
72+ expect ( innerHTMLCalls . length ) . toBe ( 1 ) ;
73+ // Ensure it didn't get stringified when passed to a DOM sink:
74+ expect ( innerHTMLCalls [ 0 ] ) . toBe ( ttObject1 ) ;
75+
76+ innerHTMLCalls . length = 0 ;
77+ ReactDOM . render (
78+ < div dangerouslySetInnerHTML = { { __html : ttObject2 } } /> ,
79+ container ,
80+ ) ;
81+ expect ( container . innerHTML ) . toBe ( '<div><b>Bye</b></div>' ) ;
82+ expect ( innerHTMLCalls . length ) . toBe ( 1 ) ;
83+ // Ensure it didn't get stringified when passed to a DOM sink:
84+ expect ( innerHTMLCalls [ 0 ] ) . toBe ( ttObject2 ) ;
85+ } finally {
86+ Object . defineProperty (
87+ Element . prototype ,
88+ 'innerHTML' ,
89+ innerHTMLDescriptor ,
90+ ) ;
91+ }
92+ } ) ;
93+
94+ it ( 'should not stringify trusted values for setAttribute (unknown attribute)' , ( ) => {
95+ let setAttribute = Element . prototype . setAttribute ;
96+ try {
97+ const setAttributeCalls = [ ] ;
98+ Element . prototype . setAttribute = function ( name , value ) {
99+ setAttributeCalls . push ( [ this , name . toLowerCase ( ) , value ] ) ;
100+ return setAttribute . apply ( this , arguments ) ;
101+ } ;
102+ ReactDOM . render ( < div data-foo = { ttObject1 } /> , container ) ;
103+ expect ( container . innerHTML ) . toBe ( '<div data-foo="<b>Hi</b>"></div>' ) ;
104+ expect ( setAttributeCalls . length ) . toBe ( 1 ) ;
105+ expect ( setAttributeCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
106+ expect ( setAttributeCalls [ 0 ] [ 1 ] ) . toBe ( 'data-foo' ) ;
107+ // Ensure it didn't get stringified when passed to a DOM sink:
108+ expect ( setAttributeCalls [ 0 ] [ 2 ] ) . toBe ( ttObject1 ) ;
109+
110+ setAttributeCalls . length = 0 ;
111+ ReactDOM . render ( < div data-foo = { ttObject2 } /> , container ) ;
112+ expect ( setAttributeCalls . length ) . toBe ( 1 ) ;
113+ expect ( setAttributeCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
114+ expect ( setAttributeCalls [ 0 ] [ 1 ] ) . toBe ( 'data-foo' ) ;
115+ // Ensure it didn't get stringified when passed to a DOM sink:
116+ expect ( setAttributeCalls [ 0 ] [ 2 ] ) . toBe ( ttObject2 ) ;
117+ } finally {
118+ Element . prototype . setAttribute = setAttribute ;
32119 }
120+ } ) ;
121+
122+ it ( 'should not stringify trusted values for setAttribute (known attribute)' , ( ) => {
123+ let setAttribute = Element . prototype . setAttribute ;
124+ try {
125+ const setAttributeCalls = [ ] ;
126+ Element . prototype . setAttribute = function ( name , value ) {
127+ setAttributeCalls . push ( [ this , name . toLowerCase ( ) , value ] ) ;
128+ return setAttribute . apply ( this , arguments ) ;
129+ } ;
130+ ReactDOM . render ( < div className = { ttObject1 } /> , container ) ;
131+ expect ( container . innerHTML ) . toBe ( '<div class="<b>Hi</b>"></div>' ) ;
132+ expect ( setAttributeCalls . length ) . toBe ( 1 ) ;
133+ expect ( setAttributeCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
134+ expect ( setAttributeCalls [ 0 ] [ 1 ] ) . toBe ( 'class' ) ;
135+ // Ensure it didn't get stringified when passed to a DOM sink:
136+ expect ( setAttributeCalls [ 0 ] [ 2 ] ) . toBe ( ttObject1 ) ;
137+
138+ setAttributeCalls . length = 0 ;
139+ ReactDOM . render ( < div className = { ttObject2 } /> , container ) ;
140+ expect ( setAttributeCalls . length ) . toBe ( 1 ) ;
141+ expect ( setAttributeCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
142+ expect ( setAttributeCalls [ 0 ] [ 1 ] ) . toBe ( 'class' ) ;
143+ // Ensure it didn't get stringified when passed to a DOM sink:
144+ expect ( setAttributeCalls [ 0 ] [ 2 ] ) . toBe ( ttObject2 ) ;
145+ } finally {
146+ Element . prototype . setAttribute = setAttribute ;
147+ }
148+ } ) ;
33149
34- const isHTMLSpy = jest . spyOn ( window . trustedTypes , [ 'isHTML' ] ) ;
35- const instance = ReactDOM . render ( < Component /> , container ) ;
36- instance . setState ( { inner : trustedObject } ) ;
150+ it ( 'should not stringify trusted values for setAttributeNS' , ( ) => {
151+ let setAttributeNS = Element . prototype . setAttributeNS ;
152+ try {
153+ const setAttributeNSCalls = [ ] ;
154+ Element . prototype . setAttributeNS = function ( ns , name , value ) {
155+ setAttributeNSCalls . push ( [ this , ns , name , value ] ) ;
156+ return setAttributeNS . apply ( this , arguments ) ;
157+ } ;
158+ ReactDOM . render ( < svg xlinkHref = { ttObject1 } /> , container ) ;
159+ expect ( container . innerHTML ) . toBe ( '<svg xlink:href="<b>Hi</b>"></svg>' ) ;
160+ expect ( setAttributeNSCalls . length ) . toBe ( 1 ) ;
161+ expect ( setAttributeNSCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
162+ expect ( setAttributeNSCalls [ 0 ] [ 1 ] ) . toBe ( 'http://www.w3.org/1999/xlink' ) ;
163+ expect ( setAttributeNSCalls [ 0 ] [ 2 ] ) . toBe ( 'xlink:href' ) ;
164+ // Ensure it didn't get stringified when passed to a DOM sink:
165+ expect ( setAttributeNSCalls [ 0 ] [ 3 ] ) . toBe ( ttObject1 ) ;
37166
38- expect ( container . firstChild . innerHTML ) . toBe ( trustedObject . toString ( ) ) ;
39- expect ( isHTMLSpy ) . toHaveBeenCalledWith ( trustedObject ) ;
167+ setAttributeNSCalls . length = 0 ;
168+ ReactDOM . render ( < svg xlinkHref = { ttObject2 } /> , container ) ;
169+ expect ( setAttributeNSCalls . length ) . toBe ( 1 ) ;
170+ expect ( setAttributeNSCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
171+ expect ( setAttributeNSCalls [ 0 ] [ 1 ] ) . toBe ( 'http://www.w3.org/1999/xlink' ) ;
172+ expect ( setAttributeNSCalls [ 0 ] [ 2 ] ) . toBe ( 'xlink:href' ) ;
173+ // Ensure it didn't get stringified when passed to a DOM sink:
174+ expect ( setAttributeNSCalls [ 0 ] [ 3 ] ) . toBe ( ttObject2 ) ;
175+ } finally {
176+ Element . prototype . setAttributeNS = setAttributeNS ;
177+ }
40178 } ) ;
41179
42180 describe ( 'dangerouslySetInnerHTML in svg elements in Internet Explorer' , ( ) => {
@@ -81,6 +219,7 @@ describe('when Trusted Types are available in global object', () => {
81219 "You can try to wrap your svg element inside a div and use 'dangerouslySetInnerHTML' " +
82220 'on the enclosing div instead.' ,
83221 ) ;
222+ expect ( container . innerHTML ) . toBe ( '<svg>unsafe html</svg>' ) ;
84223 } ) ;
85224 } ) ;
86225
@@ -95,7 +234,7 @@ describe('when Trusted Types are available in global object', () => {
95234 ' in script (at **)' ,
96235 ) ;
97236
98- // check that the warning is print only once
237+ // check that the warning is printed only once
99238 ReactDOM . render ( < script > alert("I am not executed")</ script > , container ) ;
100239 } ) ;
101240} ) ;
0 commit comments