@@ -17,22 +17,35 @@ 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
47
ReactFeatureFlags . enableTrustedTypesIntegration = true ;
48
+ ReactFeatureFlags . disableJavaScriptURLs = true ;
36
49
React = require ( 'react' ) ;
37
50
ReactDOM = require ( 'react-dom' ) ;
38
51
ttObject1 = {
@@ -152,6 +165,56 @@ describe('when Trusted Types are available in global object', () => {
152
165
}
153
166
} ) ;
154
167
168
+ it ( 'should not stringify attributes that go through sanitizeURL' , ( ) => {
169
+ const setAttribute = Element . prototype . setAttribute ;
170
+ try {
171
+ const setAttributeCalls = [ ] ;
172
+ Element . prototype . setAttribute = function ( name , value ) {
173
+ setAttributeCalls . push ( [ this , name . toLowerCase ( ) , value ] ) ;
174
+ return setAttribute . apply ( this , arguments ) ;
175
+ } ;
176
+ const trustedScriptUrlHttps = {
177
+ toString : ( ) => 'https://ok.example' ,
178
+ } ;
179
+ fakeTTObjects . add ( trustedScriptUrlHttps ) ;
180
+ // It's not a matching type (under Trusted Types a.href a string will do),
181
+ // but a.href undergoes URL and we're only testing if the value was
182
+ // passed unmodified to setAttribute.
183
+ ReactDOM . render ( < a href = { trustedScriptUrlHttps } /> , container ) ;
184
+ expect ( setAttributeCalls . length ) . toBe ( 1 ) ;
185
+ expect ( setAttributeCalls [ 0 ] [ 0 ] ) . toBe ( container . firstChild ) ;
186
+ expect ( setAttributeCalls [ 0 ] [ 1 ] ) . toBe ( 'href' ) ;
187
+ // Ensure it didn't get stringified when passed to a DOM sink:
188
+ expect ( setAttributeCalls [ 0 ] [ 2 ] ) . toBe ( trustedScriptUrlHttps ) ;
189
+ } finally {
190
+ Element . prototype . setAttribute = setAttribute ;
191
+ }
192
+ } ) ;
193
+
194
+ it ( 'should sanitize attributes even if they are Trusted Types' , ( ) => {
195
+ const setAttribute = Element . prototype . setAttribute ;
196
+ try {
197
+ const setAttributeCalls = [ ] ;
198
+ Element . prototype . setAttribute = function ( name , value ) {
199
+ setAttributeCalls . push ( [ this , name . toLowerCase ( ) , value ] ) ;
200
+ return setAttribute . apply ( this , arguments ) ;
201
+ } ;
202
+ const trustedScriptUrlJavascript = {
203
+ // eslint-disable-next-line no-script-url
204
+ toString : ( ) => 'javascript:notfine' ,
205
+ } ;
206
+ fakeTTObjects . add ( trustedScriptUrlJavascript ) ;
207
+ // Assert that the URL sanitization will correctly unwrap and verify the
208
+ // value.
209
+ expectToReject ( ( ) => {
210
+ ReactDOM . render ( < a href = { trustedScriptUrlJavascript } /> , container ) ;
211
+ } ) ;
212
+ expect ( setAttributeCalls . length ) . toBe ( 0 ) ;
213
+ } finally {
214
+ Element . prototype . setAttribute = setAttribute ;
215
+ }
216
+ } ) ;
217
+
155
218
it ( 'should not stringify trusted values for setAttributeNS' , ( ) => {
156
219
const setAttributeNS = Element . prototype . setAttributeNS ;
157
220
try {
0 commit comments