beacon-cors.https.window.js (5404B)
1 // META: timeout=long 2 // META: script=/common/get-host-info.sub.js 3 // META: script=/common/utils.js 4 // META: script=beacon-common.sub.js 5 6 'use strict'; 7 8 const {HTTPS_ORIGIN, ORIGIN, HTTPS_REMOTE_ORIGIN} = get_host_info(); 9 10 // As /common/redirect.py is not under our control, let's make sure that 11 // it doesn't support CORS. 12 parallelPromiseTest(async (t) => { 13 const destination = `${HTTPS_REMOTE_ORIGIN}/common/text-plain.txt` + 14 `?pipe=header(access-control-allow-origin,*)`; 15 const redirect = `${HTTPS_REMOTE_ORIGIN}/common/redirect.py` + 16 `?location=${encodeURIComponent(destination)}`; 17 18 // Fetching `destination` is fine because it supports CORS. 19 await fetch(destination); 20 21 // Fetching redirect.py should fail because it doesn't support CORS. 22 await promise_rejects_js(t, TypeError, fetch(redirect)); 23 }, '/common/redirect.py does not support CORS'); 24 25 for (const type of [STRING, ARRAYBUFFER, FORM, BLOB]) { 26 parallelPromiseTest(async (t) => { 27 const iframe = document.createElement('iframe'); 28 document.body.appendChild(iframe); 29 t.add_cleanup(() => iframe.remove()); 30 31 const payload = makePayload(SMALL, type); 32 const id = token(); 33 // As we use "no-cors" for CORS-safelisted requests, the redirect is 34 // processed without an error while the request is cross-origin and the 35 // redirect handler doesn't support CORS. 36 const destination = 37 `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py?cmd=store&id=${id}`; 38 const url = `${HTTPS_REMOTE_ORIGIN}/common/redirect.py` + 39 `?status=307&location=${encodeURIComponent(destination)}`; 40 41 assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload)); 42 iframe.remove(); 43 44 await waitForResult(id); 45 }, `cross-origin, CORS-safelisted: type = ${type}`); 46 } 47 48 parallelPromiseTest(async (t) => { 49 const iframe = document.createElement('iframe'); 50 document.body.appendChild(iframe); 51 t.add_cleanup(() => iframe.remove()); 52 53 const payload = makePayload(SMALL, BLOB, 'application/octet-stream'); 54 const id = token(); 55 const destination = 56 `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py?cmd=store&id=${id}`; 57 const url = `${HTTPS_REMOTE_ORIGIN}/common/redirect.py` + 58 `?status=307&location=${encodeURIComponent(destination)}`; 59 assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload)); 60 iframe.remove(); 61 62 // The beacon is rejected during redirect handling because /common/redirect.py 63 // doesn't support CORS. 64 65 await new Promise((resolve) => step_timeout(resolve, 3000)); 66 const res = await fetch(`/beacon/resources/beacon.py?cmd=stat&id=${id}`); 67 assert_equals((await res.json()).length, 0); 68 }, `cross-origin, non-CORS-safelisted: failure case (with redirect)`); 69 70 parallelPromiseTest(async (t) => { 71 const iframe = document.createElement('iframe'); 72 document.body.appendChild(iframe); 73 t.add_cleanup(() => iframe.remove()); 74 75 const payload = makePayload(SMALL, BLOB, 'application/octet-stream'); 76 const id = token(); 77 const url = 78 `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py?cmd=store&id=${id}`; 79 assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload)); 80 iframe.remove(); 81 82 // The beacon is rejected during preflight handling. 83 await waitForResult(id, /*expectedError=*/ 'Preflight not expected.'); 84 }, `cross-origin, non-CORS-safelisted: failure case (without redirect)`); 85 86 for (const credentials of [false, true]) { 87 parallelPromiseTest(async (t) => { 88 const iframe = document.createElement('iframe'); 89 document.body.appendChild(iframe); 90 t.add_cleanup(() => iframe.remove()); 91 92 const payload = makePayload(SMALL, BLOB, 'application/octet-stream'); 93 const id = token(); 94 let url = `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py` + 95 `?cmd=store&id=${id}&preflightExpected&origin=${ORIGIN}`; 96 if (credentials) { 97 url += `&credentials=true`; 98 } 99 assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload)); 100 iframe.remove(); 101 102 // We need access-control-allow-credentials in the preflight response. This 103 // shows that the request's credentials mode is 'include'. 104 if (credentials) { 105 const result = await waitForResult(id); 106 assert_equals(result.type, 'application/octet-stream'); 107 } else { 108 await new Promise((resolve) => step_timeout(resolve, 3000)); 109 const res = await fetch(`/beacon/resources/beacon.py?cmd=stat&id=${id}`); 110 assert_equals((await res.json()).length, 0); 111 } 112 }, `cross-origin, non-CORS-safelisted[credentials=${credentials}]`); 113 } 114 115 parallelPromiseTest(async (t) => { 116 const iframe = document.createElement('iframe'); 117 document.body.appendChild(iframe); 118 t.add_cleanup(() => iframe.remove()); 119 120 const payload = makePayload(SMALL, BLOB, 'application/octet-stream'); 121 const id = token(); 122 const destination = `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py` + 123 `?cmd=store&id=${id}&preflightExpected&origin=${ORIGIN}&credentials=true`; 124 const url = `${HTTPS_REMOTE_ORIGIN}/fetch/api/resources/redirect.py` + 125 `?redirect_status=307&allow_headers=content-type` + 126 `&location=${encodeURIComponent(destination)}`; 127 assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload)); 128 iframe.remove(); 129 130 const result = await waitForResult(id); 131 assert_equals(result.type, 'application/octet-stream'); 132 }, `cross-origin, non-CORS-safelisted success-case (with redirect)`);