Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// META: timeout=long
// META: script=/common/get-host-info.sub.js
// META: script=/common/utils.js
// META: script=beacon-common.sub.js
'use strict';
const {HTTPS_ORIGIN, ORIGIN, HTTPS_REMOTE_ORIGIN} = get_host_info();
// As /common/redirect.py is not under our control, let's make sure that
// it doesn't support CORS.
parallelPromiseTest(async (t) => {
const destination = `${HTTPS_REMOTE_ORIGIN}/common/text-plain.txt` +
`?pipe=header(access-control-allow-origin,*)`;
const redirect = `${HTTPS_REMOTE_ORIGIN}/common/redirect.py` +
`?location=${encodeURIComponent(destination)}`;
// Fetching `destination` is fine because it supports CORS.
await fetch(destination);
// Fetching redirect.py should fail because it doesn't support CORS.
await promise_rejects_js(t, TypeError, fetch(redirect));
}, '/common/redirect.py does not support CORS');
for (const type of [STRING, ARRAYBUFFER, FORM, BLOB]) {
parallelPromiseTest(async (t) => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
t.add_cleanup(() => iframe.remove());
const payload = makePayload(SMALL, type);
const id = token();
// As we use "no-cors" for CORS-safelisted requests, the redirect is
// processed without an error while the request is cross-origin and the
// redirect handler doesn't support CORS.
const destination =
`${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py?cmd=store&id=${id}`;
const url = `${HTTPS_REMOTE_ORIGIN}/common/redirect.py` +
`?status=307&location=${encodeURIComponent(destination)}`;
assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload));
iframe.remove();
await waitForResult(id);
}, `cross-origin, CORS-safelisted: type = ${type}`);
}
parallelPromiseTest(async (t) => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
t.add_cleanup(() => iframe.remove());
const payload = makePayload(SMALL, BLOB, 'application/octet-stream');
const id = token();
const destination =
`${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py?cmd=store&id=${id}`;
const url = `${HTTPS_REMOTE_ORIGIN}/common/redirect.py` +
`?status=307&location=${encodeURIComponent(destination)}`;
assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload));
iframe.remove();
// The beacon is rejected during redirect handling because /common/redirect.py
// doesn't support CORS.
await new Promise((resolve) => step_timeout(resolve, 3000));
const res = await fetch(`/beacon/resources/beacon.py?cmd=stat&id=${id}`);
assert_equals((await res.json()).length, 0);
}, `cross-origin, non-CORS-safelisted: failure case (with redirect)`);
parallelPromiseTest(async (t) => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
t.add_cleanup(() => iframe.remove());
const payload = makePayload(SMALL, BLOB, 'application/octet-stream');
const id = token();
const url =
`${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py?cmd=store&id=${id}`;
assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload));
iframe.remove();
// The beacon is rejected during preflight handling.
await waitForResult(id, /*expectedError=*/ 'Preflight not expected.');
}, `cross-origin, non-CORS-safelisted: failure case (without redirect)`);
for (const credentials of [false, true]) {
parallelPromiseTest(async (t) => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
t.add_cleanup(() => iframe.remove());
const payload = makePayload(SMALL, BLOB, 'application/octet-stream');
const id = token();
let url = `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py` +
`?cmd=store&id=${id}&preflightExpected&origin=${ORIGIN}`;
if (credentials) {
url += `&credentials=true`;
}
assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload));
iframe.remove();
// We need access-control-allow-credentials in the preflight response. This
// shows that the request's credentials mode is 'include'.
if (credentials) {
const result = await waitForResult(id);
assert_equals(result.type, 'application/octet-stream');
} else {
await new Promise((resolve) => step_timeout(resolve, 3000));
const res = await fetch(`/beacon/resources/beacon.py?cmd=stat&id=${id}`);
assert_equals((await res.json()).length, 0);
}
}, `cross-origin, non-CORS-safelisted[credentials=${credentials}]`);
}
parallelPromiseTest(async (t) => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
t.add_cleanup(() => iframe.remove());
const payload = makePayload(SMALL, BLOB, 'application/octet-stream');
const id = token();
const destination = `${HTTPS_REMOTE_ORIGIN}/beacon/resources/beacon.py` +
`?cmd=store&id=${id}&preflightExpected&origin=${ORIGIN}&credentials=true`;
const url = `${HTTPS_REMOTE_ORIGIN}/fetch/api/resources/redirect.py` +
`?redirect_status=307&allow_headers=content-type` +
`&location=${encodeURIComponent(destination)}`;
assert_true(iframe.contentWindow.navigator.sendBeacon(url, payload));
iframe.remove();
const result = await waitForResult(id);
assert_equals(result.type, 'application/octet-stream');
}, `cross-origin, non-CORS-safelisted success-case (with redirect)`);