@@ -17,22 +17,34 @@ describe('when Trusted Types are available in global object', () => {
17
17
let ttObject1 ;
18
18
let ttObject2 ;
19
19
20
+ const expectToReject = fn => {
21
+ let msg ;
22
+ try {
23
+ fn ( ) ;
24
+ } catch ( x ) {
25
+ msg = x . message ;
26
+ }
27
+ expect ( msg ) . toContain (
28
+ 'React has blocked a javascript: URL as a security precaution.' ,
29
+ ) ;
30
+ } ;
31
+
20
32
beforeEach ( ( ) => {
21
33
jest . resetModules ( ) ;
22
34
container = document . createElement ( 'div' ) ;
23
- const fakeTTObjects = new Set ( ) ;
35
+ fakeTTObjects = new Set ( ) ;
24
36
window . trustedTypes = {
25
37
isHTML : function ( value ) {
26
38
if ( this !== window . trustedTypes ) {
27
39
throw new Error ( this ) ;
28
40
}
29
41
return fakeTTObjects . has ( value ) ;
30
42
} ,
31
- isScript : ( ) => false ,
32
- isScriptURL : ( ) => false ,
43
+ isScript : value => fakeTTObjects . has ( value ) ,
44
+ isScriptURL : value => fakeTTObjects . has ( value ) ,
33
45
} ;
34
46
ReactFeatureFlags = require ( 'shared/ReactFeatureFlags' ) ;
35
- ReactFeatureFlags . enableTrustedTypesIntegration = true ;
47
+ ReactFeatureFlags . disableJavaScriptURLs = true ;
36
48
React = require ( 'react' ) ;
37
49
ReactDOM = require ( 'react-dom' ) ;
38
50
ttObject1 = {
@@ -152,6 +164,56 @@ describe('when Trusted Types are available in global object', () => {
152
164
}
153
165
} ) ;
154
166
167
+ it ( 'should not stringify attributes that go through sanitizeURL' , ( ) => {
168
+ const setAttribute = Element . prototype . setAttribute ;
169
+ try {
170
+ const setAttributeCalls = [ ] ;
171
+ Element . prototype . setAttribute = function ( name , value ) {
172
+ setAttributeCalls . push ( [ this , name . toLowerCase ( ) , value ] ) ;
173
+ return setAttribute . apply ( this , arguments ) ;
174
+ } ;
175
+ const trustedScriptUrlHttps = {
176
+ toString : ( ) => 'https://ok.example' ,
177
+ } ;
178
+ fakeTTObjects . add ( trustedScriptUrlHttps ) ;
179
+ // It's not a matching type (under Trusted Types a.href a string will do),
180
+ // but a.href undergoes URL and we're only testing if the value was
181
+ // passed unmodified to setAttribute.
182
+ ReactDOM . render ( < a href = { trustedScriptUrlHttps } /> , container ) ;
183
+ expect ( setAttributeCalls . length ) . toBe ( 1 ) ;
184
+ expect ( setAttributeCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
185
+ expect ( setAttributeCalls [ 0 ] [ 1 ] ) . toBe ( 'href' ) ;
186
+ // Ensure it didn't get stringified when passed to a DOM sink:
187
+ expect ( setAttributeCalls [ 0 ] [ 2 ] ) . toBe ( trustedScriptUrlHttps ) ;
188
+ } finally {
189
+ Element . prototype . setAttribute = setAttribute ;
190
+ }
191
+ } ) ;
192
+
193
+ it ( 'should sanitize attributes even if they are Trusted Types' , ( ) => {
194
+ const setAttribute = Element . prototype . setAttribute ;
195
+ try {
196
+ const setAttributeCalls = [ ] ;
197
+ Element . prototype . setAttribute = function ( name , value ) {
198
+ setAttributeCalls . push ( [ this , name . toLowerCase ( ) , value ] ) ;
199
+ return setAttribute . apply ( this , arguments ) ;
200
+ } ;
201
+ const trustedScriptUrlJavascript = {
202
+ // eslint-disable-next-line no-script-url
203
+ toString : ( ) => 'javascript:notfine' ,
204
+ } ;
205
+ fakeTTObjects . add ( trustedScriptUrlJavascript ) ;
206
+ // Assert that the URL sanitization will correctly unwrap and verify the
207
+ // value.
208
+ expectToReject ( ( ) => {
209
+ ReactDOM . render ( < a href = { trustedScriptUrlJavascript } /> , container ) ;
210
+ } ) ;
211
+ expect ( setAttributeCalls . length ) . toBe ( 0 ) ;
212
+ } finally {
213
+ Element . prototype . setAttribute = setAttribute ;
214
+ }
215
+ } ) ;
216
+
155
217
it ( 'should not stringify trusted values for setAttributeNS' , ( ) => {
156
218
const setAttributeNS = Element . prototype . setAttributeNS ;
157
219
try {
0 commit comments