storage-access-beyond-cookies-iframe-iframe.html (18468B)
1 <!doctype html> 2 <meta charset="utf-8"> 3 <script src="/resources/testdriver.js"></script> 4 <script src="/resources/testdriver-vendor.js"></script> 5 <script src="/storage-access-api/helpers.js"></script> 6 <script> 7 (async function() { 8 test_driver.set_test_context(window.top); 9 const type = (new URLSearchParams(window.location.search)).get("type"); 10 const id = (new URLSearchParams(window.location.search)).get("id"); 11 let message = "HasAccess for " + type; 12 // Step 6 (storage-access-api/storage-access-beyond-cookies.{}.sub.https.html) 13 try { 14 await test_driver.set_permission({ name: 'storage-access' }, 'granted'); 15 switch (type) { 16 case "none": { 17 let couldRequestStorageAccessForNone = true; 18 try { 19 await test_driver.bless("fake user interaction", () => document.requestStorageAccess({})); 20 } catch (_) { 21 couldRequestStorageAccessForNone = false; 22 } 23 if (couldRequestStorageAccessForNone) { 24 message = "Requesting access for {} should fail." 25 } 26 let couldRequestStorageAccessForAllFalse = true; 27 try { 28 await test_driver.bless("fake user interaction", () => document.requestStorageAccess({all:false})); 29 } catch (_) { 30 couldRequestStorageAccessForAllFalse = false; 31 } 32 if (couldRequestStorageAccessForAllFalse) { 33 message = "Requesting access for {all:false} should fail." 34 } 35 break; 36 } 37 case "cookies": { 38 await test_driver.bless("fake user interaction", () => document.requestStorageAccess({cookies: true})); 39 if (!(await document.hasUnpartitionedCookieAccess()) || !document.cookie.includes("test="+id)) { 40 message = "First-party cookies should be readable if cookies were requested."; 41 } 42 break; 43 } 44 case "sessionStorage": { 45 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({sessionStorage: true})); 46 if (id != handle.sessionStorage.getItem("test")) { 47 message = "No first-party Session Storage access"; 48 } 49 if (!!window.sessionStorage.getItem("test")) { 50 message = "Handle should not override window Session Storage"; 51 } 52 window.onstorage = (e) => { 53 if (e.key == "window_event") { 54 if (e.newValue != id) { 55 handle.sessionStorage.setItem("handle_event", "wrong data " + e.newValue); 56 } else if (e.storageArea != handle.sessionStorage) { 57 handle.sessionStorage.setItem("handle_event", "storage area mismatch"); 58 } else { 59 handle.sessionStorage.setItem("handle_event", id); 60 } 61 } 62 }; 63 break; 64 } 65 case "localStorage": { 66 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({localStorage: true})); 67 if (id != handle.localStorage.getItem("test")) { 68 message = "No first-party Local Storage access"; 69 } 70 if (!!window.localStorage.getItem("test")) { 71 message = "Handle should not override window Local Storage"; 72 } 73 window.onstorage = (e) => { 74 if (e.key == "window_event") { 75 if (e.newValue != id) { 76 handle.localStorage.setItem("handle_event", "wrong data " + e.newValue); 77 } else if (e.storageArea != handle.localStorage) { 78 handle.localStorage.setItem("handle_event", "storage area mismatch"); 79 } else { 80 handle.localStorage.setItem("handle_event", id); 81 } 82 } 83 }; 84 break; 85 } 86 case "indexedDB": { 87 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({indexedDB: true})); 88 const handle_dbs = await handle.indexedDB.databases(); 89 if (handle_dbs.length != 1 || handle_dbs[0].name != id) { 90 message = "No first-party IndexedDB access"; 91 } 92 const local_dbs = await window.indexedDB.databases(); 93 if (local_dbs.length != 0) { 94 message = "Handle should not override window IndexedDB"; 95 } 96 await handle.indexedDB.deleteDatabase(id); 97 break; 98 } 99 case "locks": { 100 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({locks: true})); 101 const handle_state = await handle.locks.query(); 102 if (handle_state.held.length != 1 || handle_state.held[0].name != id) { 103 message = "No first-party Web Lock access"; 104 } 105 const local_state = await window.navigator.locks.query(); 106 if (local_state.held.length != 0) { 107 message = "Handle should not override window Web Locks"; 108 } 109 await handle.locks.request(id, {steal: true}, async () => {}); 110 break; 111 } 112 case "caches": { 113 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({caches: true})); 114 const handle_has = await handle.caches.has(id); 115 if (!handle_has) { 116 message = "No first-party Cache Storage access"; 117 } 118 const local_has = await window.caches.has(id); 119 if (local_has) { 120 message = "Handle should not override window Cache Storage"; 121 } 122 document.cookie = "partitioned=test; SameSite=None; Secure; Partitioned;"; 123 const cache = await handle.caches.open(id); 124 await cache.add("/storage-access-api/resources/get_cookies.py?2"); 125 await test_driver.bless("fake user interaction", () => document.requestStorageAccess()); 126 await cache.add("/storage-access-api/resources/get_cookies.py?3"); 127 let req = await cache.match("/storage-access-api/resources/get_cookies.py?1"); 128 let req_json = await req.json(); 129 if (!req_json.hasOwnProperty("samesite_strict")) { 130 message = "Top-level cache fetch should have SameSite=Strict cookies."; 131 } 132 if (!req_json.hasOwnProperty("samesite_lax")) { 133 message = "Top-level cache fetch should have SameSite=Lax cookies."; 134 } 135 if (!req_json.hasOwnProperty("samesite_none")) { 136 message = "Top-level cache fetch should have SameSite=None cookies."; 137 } 138 if (req_json.hasOwnProperty("partitioned")) { 139 message = "Top-level cache fetch should not have partitioned cookies."; 140 } 141 req = await cache.match("/storage-access-api/resources/get_cookies.py?2"); 142 req_json = await req.json(); 143 if (req_json.hasOwnProperty("samesite_strict")) { 144 message = "SAA cache fetch should not have SameSite=Strict cookies."; 145 } 146 if (req_json.hasOwnProperty("samesite_lax")) { 147 message = "SAA cache fetch should not have SameSite=Lax cookies."; 148 } 149 // Note: no assertion about default third-party cookie behavior. 150 if (!req_json.hasOwnProperty("partitioned")) { 151 message = "SAA cache fetch should have partitioned cookies."; 152 } 153 req = await cache.match("/storage-access-api/resources/get_cookies.py?3"); 154 req_json = await req.json(); 155 if (req_json.hasOwnProperty("samesite_strict")) { 156 message = "SAA cache + cookie fetch should not have SameSite=Strict cookies."; 157 } 158 if (req_json.hasOwnProperty("samesite_lax")) { 159 message = "SAA cache + cookie fetch should not have SameSite=Lax cookies."; 160 } 161 if (!req_json.hasOwnProperty("samesite_none")) { 162 message = "SAA cache + cookie fetch should have SameSite=None cookies."; 163 } 164 if (!req_json.hasOwnProperty("partitioned")) { 165 message = "SAA cache + cookie fetch should have partitioned cookies."; 166 } 167 await handle.caches.delete(id); 168 break; 169 } 170 case "getDirectory": { 171 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({getDirectory: true})); 172 const handle_root = await handle.getDirectory(); 173 let handle_has = await handle_root.getFileHandle(id).then(() => true, () => false); 174 if (!handle_has) { 175 message = "No first-party Origin Private File System access"; 176 } 177 const local_root = await window.navigator.storage.getDirectory(); 178 let local_has = await local_root.getFileHandle(id).then(() => true, () => false); 179 if (local_has) { 180 message = "Handle should not override window Origin Private File System"; 181 } 182 await handle_root.removeEntry(id); 183 break; 184 } 185 case "estimate": { 186 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({estimate: true})); 187 const handle_estimate = await handle.estimate(); 188 if (handle_estimate.usage <= 0) { 189 message = "No first-party quota access"; 190 } 191 const local_estimate = await window.navigator.storage.estimate(); 192 if (local_estimate > 0) { 193 message = "Handle should not override window quota"; 194 } 195 break; 196 } 197 case "blobStorage": { 198 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({createObjectURL: true, revokeObjectURL: true})); 199 let blob = await fetch(atob(id)).then( 200 (response) => response.text(), 201 () => ""); 202 if (blob != "TEST") { 203 message = "Blob storage should be readable in this context"; 204 } 205 URL.revokeObjectURL(atob(id)); 206 blob = await fetch(atob(id)).then( 207 (response) => response.text(), 208 () => ""); 209 if (blob != "TEST") { 210 message = "Handle should not override window blob storage"; 211 } 212 handle.revokeObjectURL(atob(id)); 213 blob = await fetch(atob(id)).then( 214 (response) => response.text(), 215 () => ""); 216 if (blob != "") { 217 message = "No first-party blob access"; 218 } 219 const url = handle.createObjectURL(new Blob(["TEST2"])); 220 blob = await fetch(url).then( 221 (response) => response.text(), 222 () => ""); 223 if (blob != "TEST2") { 224 message = "A blob url created via the handle should be readable"; 225 } 226 handle.revokeObjectURL(url); 227 blob = await fetch(url).then( 228 (response) => response.text(), 229 () => ""); 230 if (blob != "") { 231 message = "A blob url removed via the handle should be cleared"; 232 } 233 break; 234 } 235 case "BroadcastChannel": { 236 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({BroadcastChannel: true})); 237 const handle_channel = handle.BroadcastChannel(id); 238 handle_channel.postMessage("Same-origin handle access"); 239 handle_channel.close(); 240 const local_channel = new BroadcastChannel(id); 241 local_channel.postMessage("Same-origin local access"); 242 local_channel.close(); 243 break; 244 } 245 case "SharedWorker": { 246 const local_shared_worker = new SharedWorker("/storage-access-api/resources/shared-worker-relay.js", id); 247 local_shared_worker.port.start(); 248 local_shared_worker.port.postMessage("Same-origin local access"); 249 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({SharedWorker: true})); 250 let couldRequestAllCookies = true; 251 try { 252 handle.SharedWorker("/storage-access-api/resources/shared-worker-relay.js", {name: id, sameSiteCookies: 'all'}); 253 } catch (_) { 254 couldRequestAllCookies = false; 255 } 256 if (couldRequestAllCookies) { 257 message = "Shared Workers in a third-party context should not be able to request SameSite cookies."; 258 } 259 handle.SharedWorker("/storage-access-api/resources/shared-worker-cookies.py", id).port.start(); 260 const handle_shared_worker = handle.SharedWorker("/storage-access-api/resources/shared-worker-relay.js", {name: id, sameSiteCookies: 'none'}); 261 handle_shared_worker.port.start(); 262 handle_shared_worker.port.postMessage("Same-origin handle access"); 263 break; 264 } 265 case "BlobURLDedicatedWorker": { 266 const fetch_unsuccessful_response = "fetch_unsuccessful"; 267 const fetch_successful_response = "fetch_successful"; 268 269 const can_blob_url_be_fetched_js = ` 270 onmessage = async (e) => { 271 const blob_url = e.data; 272 try { 273 const blob = await fetch(blob_url).then(response => response.blob()); 274 await blob.text(); 275 postMessage("${fetch_successful_response}"); 276 } catch(e) { 277 postMessage("${fetch_unsuccessful_response}"); 278 } 279 }; 280 `; 281 282 // case 1: create dedicated worker w/o granting storage access 283 const worker_blob_url = new Blob([can_blob_url_be_fetched_js], { type: 'text/javascript' }); 284 const third_party_blob_url = URL.createObjectURL(worker_blob_url); 285 286 const worker_1 = new Worker(third_party_blob_url); 287 288 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({all: true})); 289 290 const worker_blob = new Blob(["potato"]); 291 const first_party_blob_url = handle.createObjectURL(worker_blob); 292 293 const worker_response_promise = new Promise((resolve) => { 294 worker_1.onmessage = (e) => { resolve(e.data) }; 295 worker_1.postMessage(first_party_blob_url); 296 }); 297 298 const worker_response = await worker_response_promise; 299 if (worker_response === fetch_unsuccessful_response) { 300 message = "Dedicated worker expectedly failed fetching first-party blob URL from a third-party context without granting storage access."; 301 } else if (worker_response === fetch_successful_response) { 302 message = "Dedicated worker unexpectedly fetched first-party blob URL from a third-party context without granting storage access."; 303 break; 304 } 305 306 // case 2: create dedicated worker after storage access is granted 307 const worker_2 = new Worker(third_party_blob_url); 308 const worker_response_promise2 = new Promise((resolve) => { 309 worker_2.onmessage = (e) => { resolve(e.data) }; 310 worker_2.postMessage(first_party_blob_url); 311 }); 312 const worker_response2 = await worker_response_promise2; 313 314 URL.revokeObjectURL(third_party_blob_url); 315 handle.revokeObjectURL(first_party_blob_url); 316 worker_2.terminate(); 317 318 if (worker_response2 === fetch_unsuccessful_response) { 319 message = "Dedicated worker unexpectedly failed fetching first-party blob URL from a third-party context with granting storage access."; 320 break; 321 } else if (worker_response2 === fetch_successful_response) { 322 message = "Blob URL DedicatedWorker tests completed successfully."; 323 } 324 break; 325 } 326 case "ThirdPartyBlobURL": { 327 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({all: true})); 328 try { 329 const blob = await fetch(atob(id)).then(response => response.blob()); 330 await blob.text(); 331 } catch (e) { 332 message = "Third Party Blob URL tests completed successfully."; 333 break; 334 } 335 message = "Cross-partition third-party blob URL was accessible after granting storage access"; 336 break; 337 } 338 case "BlobURLSharedWorker": { 339 const handle = await test_driver.bless("fake user interaction", () => document.requestStorageAccess({createObjectURL: true, revokeObjectURL: true, SharedWorker: true})); 340 const workerScript = await fetch('/storage-access-api/resources/shared-worker-relay.js').then(response => response.text()); 341 const workerBlob = new Blob([workerScript], { type: 'text/javascript' }); 342 343 const firstPartyBlobUrl = handle.createObjectURL(workerBlob); 344 const thirdPartyWorker = new SharedWorker(firstPartyBlobUrl); 345 var workerCreated = true; 346 await new Promise((resolve) => { 347 thirdPartyWorker.onerror = (event) => { 348 message = "Third-party SharedWorker not created from first-party blob URL unexpectedly."; 349 workerCreated = false; 350 resolve(); 351 } 352 thirdPartyWorker.port.onmessage = (event) => { 353 message = "Third-party SharedWorker created from first-party blob URL."; 354 resolve(); 355 } 356 thirdPartyWorker.port.postMessage("ping"); 357 }); 358 if (!workerCreated) { 359 break; 360 } 361 362 const firstPartyWorker = handle.SharedWorker(firstPartyBlobUrl); 363 await new Promise((resolve) => { 364 firstPartyWorker.onerror = (event) => { 365 message = "First-party SharedWorker not created from first-party blob URL unexpectedly."; 366 workerCreated = false; 367 resolve(); 368 } 369 firstPartyWorker.port.onmessage = (event) => { 370 message = "First-party SharedWorker created from first-party blob URL."; 371 resolve(); 372 } 373 firstPartyWorker.port.postMessage("ping"); 374 }); 375 if (!workerCreated) { 376 break; 377 } 378 379 const thirdPartyBlobUrl = URL.createObjectURL(workerBlob); 380 const worker = handle.SharedWorker(thirdPartyBlobUrl); 381 await new Promise((resolve) => { 382 worker.onerror = (event) => { 383 message = "Blob URL SharedWorker tests completed successfully."; 384 workerCreated = false; 385 resolve(); 386 }; 387 worker.port.onmessage = (event) => { 388 message = "First-party SharedWorker created from third-party blob URL unexpectedly."; 389 resolve(); 390 } 391 worker.port.postMessage("ping"); 392 }); 393 break; 394 } 395 default: { 396 message = "Unexpected type " + type; 397 break; 398 } 399 } 400 } catch (_) { 401 message = "Unable to load handle in same-origin context for " + type; 402 } 403 // Step 7 (storage-access-api/storage-access-beyond-cookies.{}.sub.https.html) 404 await test_driver.set_permission({ name: 'storage-access' }, 'prompt'); 405 window.top.postMessage({type: "result", message: message}, "*"); 406 })(); 407 </script>