file_main.html (11553B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 Tests for Mixed Content Blocker 5 https://bugzilla.mozilla.org/show_bug.cgi?id=62178 6 --> 7 <head> 8 <meta charset="utf-8"> 9 <title>Tests for Bug 62178</title> 10 <script src="/tests/SimpleTest/EventUtils.js"></script> 11 </head> 12 <body> 13 <div id="testContent"></div> 14 15 <!-- types the Mixed Content Blocker can block 16 /* 17 switch (aContentType) { 18 case nsIContentPolicy::TYPE_OBJECT: 19 case nsIContentPolicy::TYPE_SCRIPT: 20 case nsIContentPolicy::TYPE_STYLESHEET: 21 case nsIContentPolicy::TYPE_SUBDOCUMENT: 22 case nsIContentPolicy::TYPE_XMLHTTPREQUEST: 23 24 case nsIContentPolicy::TYPE_FONT: - NO TEST: 25 Load events for external fonts are not detectable by javascript. 26 case nsIContentPolicy::TYPE_WEBSOCKET: - NO TEST: 27 websocket connections over https require an encrypted websocket protocol (wss:) 28 29 case nsIContentPolicy::TYPE_IMAGE: 30 case nsIContentPolicy::TYPE_IMAGESET: 31 case nsIContentPolicy::TYPE_MEDIA: 32 case nsIContentPolicy::TYPE_PING: 33 our ping implementation is off by default and does not comply with the current spec (bug 786347) 34 case nsIContentPolicy::TYPE_BEACON: 35 36 } 37 */ 38 --> 39 40 <script> 41 function ok(a, msg) { 42 parent.postMessage({msg, check: true, status: !!a}, "http://mochi.test:8888"); 43 } 44 45 function is(a, b, msg) { 46 ok(a == b, msg); 47 } 48 49 function report(type, msg) { 50 parent.postMessage({test: type, msg}, "http://mochi.test:8888"); 51 } 52 53 function uniqueID() { 54 return Math.floor(Math.random() * 100000) 55 } 56 function uniqueIDParam(id) { 57 return "&uniqueID=" + id; 58 } 59 60 async function init() { 61 var baseUrl = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs"; 62 var checkLastRequestUrl = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs?lastRequest=true"; 63 64 //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds. 65 var MAX_COUNT = 100; 66 var TIMEOUT_INTERVAL = 100; 67 68 var testContent = document.getElementById("testContent"); 69 70 async function checkLastRequest() { 71 const response = await fetch(checkLastRequestUrl); 72 return response.json(); 73 } 74 75 /* Part 1: Mixed Script tests */ 76 77 // Test 1a: insecure object 78 var object = document.createElement("object"); 79 var objectId = uniqueID(); 80 object.data = baseUrl + "?type=object" + uniqueIDParam(objectId); 81 object.type = "image/png"; 82 object.width = "200"; 83 object.height = "200"; 84 85 testContent.appendChild(object); 86 87 var objectCount = 0; 88 89 function objectStatus(object) { 90 // Expose our privileged bits on the object. We will match the MIME type to the one 91 // provided by file_server.sjs 92 object = SpecialPowers.wrap(object); 93 var typeIsSet = object.actualType && (object.actualType !== ''); 94 var isLoaded = typeIsSet && object.actualType === 'application/x-test-match'; 95 if (isLoaded) { 96 //object loaded 97 report("object", "insecure object loaded"); 98 } 99 else { 100 if(!typeIsSet && objectCount < MAX_COUNT) { 101 objectCount++; 102 setTimeout(objectStatus, TIMEOUT_INTERVAL, object); 103 } 104 else { 105 //After we have called setTimeout the maximum number of times, assume object is blocked 106 report("object", "insecure object blocked"); 107 } 108 } 109 } 110 111 // object does not have onload and onerror events. Hence we need a setTimeout to check the object's status 112 setTimeout(objectStatus, TIMEOUT_INTERVAL, object); 113 114 // Test 1b: insecure script 115 var script = document.createElement("script"); 116 var scriptId = uniqueID(); 117 var scriptLoad = false; 118 var scriptCount = 0; 119 script.src = baseUrl + "?type=script" + uniqueIDParam(scriptId); 120 script.onload = function(e) { 121 report("script", "insecure script loaded"); 122 scriptLoad = true; 123 } 124 testContent.appendChild(script); 125 126 function scriptStatus(script) 127 { 128 if(scriptLoad) { 129 return; 130 } 131 if (scriptCount < MAX_COUNT) { 132 scriptCount++; 133 setTimeout(scriptStatus, TIMEOUT_INTERVAL, script); 134 } 135 else { 136 //After we have called setTimeout the maximum number of times, assume script is blocked 137 report("script", "insecure script blocked"); 138 } 139 } 140 141 // scripts blocked by Content Policy's do not have onerror events (see bug 789856). Hence we need a setTimeout to check the script's status 142 setTimeout(scriptStatus, TIMEOUT_INTERVAL, script); 143 144 145 // Test 1c: insecure stylesheet 146 var cssStyleSheet = document.createElement("link"); 147 var cssStyleSheetId = uniqueID(); 148 cssStyleSheet.rel = "stylesheet"; 149 cssStyleSheet.href = baseUrl + "?type=stylesheet" + uniqueIDParam(cssStyleSheetId); 150 cssStyleSheet.type = "text/css"; 151 testContent.appendChild(cssStyleSheet); 152 153 var styleCount = 0; 154 155 function styleStatus(cssStyleSheet) { 156 if( cssStyleSheet.sheet || cssStyleSheet.styleSheet || cssStyleSheet.innerHTML ) { 157 report("stylesheet", "insecure stylesheet loaded"); 158 } 159 else { 160 if(styleCount < MAX_COUNT) { 161 styleCount++; 162 setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet); 163 } 164 else { 165 //After we have called setTimeout the maximum number of times, assume stylesheet is blocked 166 report("stylesheet", "insecure stylesheet blocked"); 167 } 168 } 169 } 170 171 // link does not have onload and onerror events. Hence we need a setTimeout to check the link's status 172 window.setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet); 173 174 // Test 1d: insecure iframe 175 var iframe = document.createElement("iframe"); 176 var iframeId = uniqueID(); 177 iframe.src = baseUrl + "?type=iframe" + uniqueIDParam(iframeId); 178 iframe.onload = function() { 179 report("iframe", "insecure iframe loaded"); 180 } 181 iframe.onerror = function() { 182 report("iframe", "insecure iframe blocked"); 183 }; 184 testContent.appendChild(iframe); 185 186 187 // Test 1e: insecure xhr 188 await new Promise((resolve) => { 189 var xhr = new XMLHttpRequest; 190 var xhrId = uniqueID(); 191 try { 192 xhr.open("GET", baseUrl + "?type=xhr" + uniqueIDParam(xhrId), true); 193 xhr.send(); 194 xhr.onloadend = function (oEvent) { 195 if (xhr.status == 200) { 196 report("xhr", "insecure xhr loaded"); 197 resolve(); 198 } 199 else { 200 report("xhr", "insecure xhr blocked"); 201 resolve(); 202 } 203 } 204 } catch(ex) { 205 report("xhr", "insecure xhr blocked"); 206 resolve(); 207 } 208 }); 209 210 /* Part 2: Mixed Display tests */ 211 212 // Shorthand for all image test variants 213 async function imgHandlers(img, test, uniqueID) { 214 await new Promise((resolve) => { 215 img.onload = async () => { 216 const lastRequest = await checkLastRequest(); 217 is(lastRequest.uniqueID, uniqueID, "UniqueID for the last request matches"); 218 let message = "insecure image loaded"; 219 if (lastRequest.scheme == "https") { 220 message = "secure image loaded after upgrade"; 221 } 222 report(test, message); 223 resolve(); 224 } 225 img.onerror = async () => { 226 let message = "insecure image blocked"; 227 report(test, message); 228 resolve(); 229 } 230 }); 231 } 232 233 // Test 2a: insecure image 234 var img = document.createElement("img"); 235 var imgId = uniqueID(); 236 img.src = baseUrl + "?type=img" + uniqueIDParam(imgId); 237 await imgHandlers(img, "image", imgId); 238 // We don't need to append the image to the document. Doing so causes the image test to run twice. 239 240 // Test 2b: insecure media 241 var media = document.createElement("video"); 242 var mediaId = uniqueID(); 243 media.src = baseUrl + "?type=media" + uniqueIDParam(mediaId); 244 media.width = "320"; 245 media.height = "200"; 246 media.type = "video/ogg"; 247 await new Promise(res => { 248 media.onloadeddata = async () => { 249 const lastRequest = await checkLastRequest(); 250 is(lastRequest.uniqueID, mediaId, "UniqueID for the last request matches"); 251 let message = "insecure media loaded"; 252 if (lastRequest.scheme == "https") { 253 message = "secure media loaded after upgrade"; 254 } 255 report("media", message); 256 res(); 257 } 258 media.onerror = function() { 259 report("media", "insecure media blocked"); 260 res(); 261 } 262 }); 263 // We don't need to append the video to the document. Doing so causes the image test to run twice. 264 265 /* Part 3: Mixed Active Tests for Image srcset */ 266 267 // Test 3a: image with srcset 268 var imgA = document.createElement("img"); 269 var imgAId = uniqueID(); 270 imgA.srcset = baseUrl + "?type=img&subtype=imageSrcset" + uniqueIDParam(imgAId); 271 await imgHandlers(imgA, "imageSrcset", imgAId); 272 273 // Test 3b: image with srcset, using fallback from src, should still use imageset policy 274 var imgB = document.createElement("img"); 275 var imgBId = uniqueID(); 276 imgB.srcset = " "; 277 imgB.src = baseUrl + "?type=img&subtype=imageSrcSetFallback" + uniqueIDParam(imgBId); 278 await imgHandlers(imgB, "imageSrcsetFallback", imgBId); 279 280 // Test 3c: image in <picture> 281 var imgC = document.createElement("img"); 282 var pictureC = document.createElement("picture"); 283 var sourceC = document.createElement("source"); 284 var sourceCId = uniqueID(); 285 sourceC.srcset = baseUrl + "?type=img&subtype=imagePicture" + uniqueIDParam(sourceCId); 286 pictureC.appendChild(sourceC); 287 pictureC.appendChild(imgC); 288 await imgHandlers(imgC, "imagePicture", sourceCId); 289 290 // Test 3d: Loaded basic image switching to a <picture>, loading 291 // same source, should still redo the request with new 292 // policy. 293 var imgD = document.createElement("img"); 294 var imgDId = uniqueID(); 295 var sourceDId = uniqueID(); 296 imgD.src = baseUrl + "?type=img&subtype=imageJoinPicture" + uniqueIDParam(imgDId); 297 await new Promise(res => { 298 imgD.onload = imgD.onerror = function() { 299 // Whether or not it loads, we want to now append it to a picture and observe 300 var pictureD = document.createElement("picture"); 301 var sourceD = document.createElement("source"); 302 sourceD.srcset = baseUrl + "?type=img&subtype=imageJoinPicture2" + uniqueIDParam(sourceDId); 303 pictureD.appendChild(sourceD); 304 pictureD.appendChild(imgD); 305 res(); 306 } 307 }); 308 await imgHandlers(imgD, "imageJoinPicture", sourceDId); 309 310 // Test 3e: img load from <picture> source reverts to img.src as it 311 // is removed -- the new request should revert to mixed 312 // display policy 313 var imgE = document.createElement("img"); 314 var pictureE = document.createElement("picture"); 315 var pictureEId = uniqueID(); 316 var sourceE = document.createElement("source"); 317 var sourceEId = uniqueID(); 318 sourceE.srcset = baseUrl + "?type=img&subtype=imageLeavePicture" + uniqueIDParam(sourceEId); 319 pictureE.appendChild(sourceE); 320 pictureE.appendChild(imgE); 321 imgE.src = baseUrl + "?type=img&subtype=imageLeavePicture2" + uniqueIDParam(pictureEId); 322 await new Promise(res => { 323 imgE.onload = imgE.onerror = function() { 324 // Whether or not it loads, remove it from the picture and observe 325 pictureE.removeChild(imgE) 326 res(); 327 } 328 }); 329 await imgHandlers(imgE, "imageLeavePicture", pictureEId); 330 } 331 332 init(); 333 334 </script> 335 </body> 336 </html>