Skip to content

Commit

Permalink
Add WPT tests for SameSite cookies in ServiceWorkers with nested frames.
Browse files Browse the repository at this point in the history
This CL adds a number of new cases to the service worker SameSite
cookies test.  The cases break down into two general types:

1. Cases where A1 frames B frames A2, and then A2 calls window.open()
   to an A origin URL.
2. Cases where A1 frames B frames A2, and then A2 sets the location
   to an A origin URL.

For (1) we expect SameSite strict cookies to be sent because
window.open() creates a top-level context that will have a populated
site-for-cookies and the initiator is same-origin (regardless of the
cross-site ancestor chain).

For (2) we expect only SameSite=None cookies to be sent.  This is
because setting the location results in a navigation to an A1->B->A3
nested frame with an empty site-for-cookies.

We currently fail the passthrough and change-request cases for (2).
We plan to fix this as part of storage partitioning with an ancestor
chain bit in the StorageKey.  See:

privacycg/storage-partitioning#25

This CL also includes some minor cleanup of the WPT test and associated
resources.

Bug: 1115847
Change-Id: I9002e60a271ae95d1d702068d44b30bd0e33b5dc
  • Loading branch information
wanderview authored and chromium-wpt-export-bot committed Nov 19, 2021
1 parent 4230a15 commit 35013f1
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 27 deletions.
1 change: 1 addition & 0 deletions service-workers/service-worker/resources/form-poster.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!DOCTYPE html>
<meta charset="utf-8"/>
<meta name="referrer" content="origin">
<form method="POST" id="form"></form>
<script>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!DOCTYPE html>
<meta charset="utf-8"/>
<meta name="referrer" content="origin">
<script>
function onLoad() {
Expand Down
18 changes: 18 additions & 0 deletions service-workers/service-worker/resources/nested-parent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta charset="utf-8"/>
<meta name="referrer" content="origin">
<script>
async function onLoad() {
self.addEventListener('message', evt => {
if (self.opener)
self.opener.postMessage(evt.data, '*');
else
self.top.postMessage(evt.data, '*');
}, { once: true });
const params = new URLSearchParams(self.location.search);
const frame = document.createElement('iframe');
frame.src = params.get('target');
document.body.appendChild(frame);
}
self.addEventListener('load', onLoad);
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,27 @@
const scope = self.origin + '/cookies/resources/postToParent.py?with-sw';
const script = './fetch-rewrite-worker.js';
const reg = await navigator.serviceWorker.register(script, { scope: scope });
await new Promise(resolve => {
const worker = reg.installing;
worker.addEventListener('statechange', evt => {
if (worker.state === 'activated') {
resolve();
}
// In nested cases we may be impacted by partitioning or not depending on
// the browser. With partitioning we will be installing a new worker here,
// but without partitioning the worker will already exist. Handle both cases.
if (reg.installing) {
await new Promise(resolve => {
const worker = reg.installing;
worker.addEventListener('statechange', evt => {
if (worker.state === 'activated') {
resolve();
}
});
});
});
if (reg.navigationPreload) {
await reg.navigationPreload.enable();
if (reg.navigationPreload) {
await reg.navigationPreload.enable();
}
}
if (window.opener) {
window.opener.postMessage({ type: 'SW-REGISTERED' }, '*');
} else {
window.top.postMessage({ type: 'SW-REGISTERED' }, '*');
}
window.opener.postMessage({ type: 'SW-REGISTERED' }, '*');
}
self.addEventListener('load', onLoad);
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
async function onLoad() {
const scope = self.origin + '/cookies/resources/postToParent.py?with-sw';
const reg = await navigator.serviceWorker.getRegistration(scope);
await reg.unregister();
window.opener.postMessage({ type: 'SW-UNREGISTERED' }, '*');
if (reg) {
await reg.unregister();
}
if (window.opener) {
window.opener.postMessage({ type: 'SW-UNREGISTERED' }, '*');
} else {
window.top.postMessage({ type: 'SW-UNREGISTERED' }, '*');
}
}
self.addEventListener('load', onLoad);
</script>
17 changes: 17 additions & 0 deletions service-workers/service-worker/resources/window-opener.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<meta charset="utf-8"/>
<meta name="referrer" content="origin">
<script>
function onLoad() {
self.onmessage = evt => {
if (self.opener)
self.opener.postMessage(evt.data, '*');
else
self.top.postMessage(evt.data, '*');
}
const params = new URLSearchParams(self.location.search);
const w = window.open(params.get('target'));
self.addEventListener('unload', evt => w.close());
}
self.addEventListener('load', onLoad);
</script>
131 changes: 116 additions & 15 deletions service-workers/service-worker/same-site-cookies.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,33 @@
<body>
<script>
'use strict';
async function unregister_service_worker(origin) {
const w = window.open(origin +
'/service-workers/service-worker/resources/same-site-cookies-unregister.html');

function make_nested_url(nested_origins, target_url) {
for (let i = nested_origins.length - 1; i >= 0; --i) {
target_url = new URL(
`./resources/nested-parent.html?target=${encodeURIComponent(target_url)}`,
nested_origins[i] + self.location.pathname);
}
return target_url;
}

async function unregister_service_worker(origin, nested_origins=[]) {
let target_url = origin +
'/service-workers/service-worker/resources/same-site-cookies-unregister.html';
target_url = make_nested_url(nested_origins, target_url);
const w = window.open(target_url);
try {
await wait_for_message('SW-UNREGISTERED');
} finally {
w.close();
}
}

async function register_service_worker(origin) {
const w = window.open(origin +
'/service-workers/service-worker/resources/same-site-cookies-register.html');
async function register_service_worker(origin, nested_origins=[]) {
let target_url = origin +
'/service-workers/service-worker/resources/same-site-cookies-register.html';
target_url = make_nested_url(nested_origins, target_url);
const w = window.open(target_url);
try {
await wait_for_message('SW-REGISTERED');
} finally {
Expand All @@ -31,7 +45,7 @@
}

async function run_test(t, origin, navaction, swaction, expected,
redirect_origins=[]) {
redirect_origins=[], nested_origins=[]) {
const value = 'COOKIE_VALUE';
await resetSameSiteCookies(origin, value);
if (swaction === 'navpreload') {
Expand Down Expand Up @@ -59,23 +73,32 @@
}

if (navaction === 'window.open') {
const w = window.open(target_url);
t.add_cleanup(() => w.close());
target_url = new URL(
`./resources/window-opener.html?target=${encodeURIComponent(target_url)}`,
self.origin + self.location.pathname);
} else if (navaction === 'form post') {
const poster_url =
`./resources/form-poster.html?target=${encodeURIComponent(target_url)}`;
const w = window.open(poster_url);
t.add_cleanup(() => w.close());
target_url = new URL(
`./resources/form-poster.html?target=${encodeURIComponent(target_url)}`,
self.origin + self.location.pathname);
} else if (navaction === 'set location') {
target_url = new URL(
`./resources/location-setter.html?target=${encodeURIComponent(target_url)}`,
self.origin + self.location.pathname);
}

const w = window.open(make_nested_url(nested_origins, target_url));
t.add_cleanup(() => w.close());

const result = await wait_for_message('COOKIES');
verifySameSiteCookieState(expected, value, result.data,
DomSameSiteStatus.SAME_SITE);
verifySameSiteCookieState(expected, value, result.data);
}

promise_test(async t => {
await register_service_worker(self.origin);
await register_service_worker(SECURE_SUBDOMAIN_ORIGIN);
await register_service_worker(SECURE_CROSS_SITE_ORIGIN);
await register_service_worker(self.origin,
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'Setup service workers');

promise_test(t => {
Expand Down Expand Up @@ -236,6 +259,82 @@
}, 'same-origin, window.open with navpreload, cross-site redirect, and ' +
'same-origin redirect');

//
// Double-nested frame calling open.window() tests
//
promise_test(t => {
return run_test(t, self.origin, 'window.open', 'no-sw',
SameSiteStatus.STRICT, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
'no service worker');

promise_test(t => {
return run_test(t, self.origin, 'window.open', 'fallback',
SameSiteStatus.STRICT, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
'fallback service worker');

promise_test(t => {
return run_test(t, self.origin, 'window.open', 'passthrough',
SameSiteStatus.STRICT, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
'passthrough service worker');

promise_test(t => {
return run_test(t, self.origin, 'window.open', 'change-request',
SameSiteStatus.STRICT, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
'change-request service worker');

promise_test(t => {
return run_test(t, self.origin, 'window.open', 'navpreload',
SameSiteStatus.STRICT, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
'navpreload service worker');

//
// Double-nested frame setting location tests
//
promise_test(t => {
return run_test(t, self.origin, 'set location', 'no-sw',
SameSiteStatus.CROSS_SITE, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
'no service worker');

promise_test(t => {
return run_test(t, self.origin, 'set location', 'fallback',
SameSiteStatus.CROSS_SITE, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
'fallback service worker');

promise_test(t => {
return run_test(t, self.origin, 'set location', 'passthrough',
SameSiteStatus.CROSS_SITE, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
'passthrough service worker');

promise_test(t => {
return run_test(t, self.origin, 'set location', 'change-request',
SameSiteStatus.CROSS_SITE, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
'change-request service worker');

promise_test(t => {
return run_test(t, self.origin, 'set location', 'navpreload',
SameSiteStatus.CROSS_SITE, [],
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
'navpreload service worker');

//
// Form POST tests
//
Expand Down Expand Up @@ -379,6 +478,8 @@
await unregister_service_worker(self.origin);
await unregister_service_worker(SECURE_SUBDOMAIN_ORIGIN);
await unregister_service_worker(SECURE_CROSS_SITE_ORIGIN);
await unregister_service_worker(self.origin,
[self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'Cleanup service workers');

</script>
Expand Down

0 comments on commit 35013f1

Please sign in to comment.